大话Java性能优化(txt+pdf+epub+mobi电子书下载)


发布时间:2020-09-30 09:03:12

点击下载

作者:周明耀

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

大话Java性能优化

大话Java性能优化试读:

前言

7岁那年,当我合上《上下五千年》一套三册书籍时,我对自己说,我想当个作家。这一晃27年了,等待了27年,我的第一本书《大话Java性能优化》即将面世了。我是多么的忐忑、惊喜,就像第一次面对我的女儿“小顽子”,给她取这个小名,希望她顽强到底,因为我相信,你若顽强到底,一切皆有可能。

从15岁拥有自己第一台电脑算起,已经有接近20年的计算机学习时间,加上11年的工作经历,我对于工作,对于工程师这个职业,有一些自己的感悟。我认为,职业素养非常重要。

1929年,在汪精卫的支持下,余云岫等人提出了全面废除中医、禁止中医的提案,并很快获得初审通过。在这样的局面下,全国各地中医师多次到南京请愿,虽有孙科等人的支持,但反响不大。相持阶段,无独有偶,汪精卫的岳母身患痢疾,西医师医治无效,京城四大名医之一的施今墨先生毅然赴汪府。施今墨凭脉,每言必中,使汪精卫的岳母心服口服,频频点头称是。处方时施今墨说:“安心服药,一诊可愈,不必复诊。”病危至此,一诊可愈?众人皆疑。据此处方仅服数剂,果如施今墨所言。汪精卫不得不服中医,最终撤回提案。施老先生医德高尚,死后遗体都捐献出来供科学研究,绝不是阿谀奉承之人,他赴汪府,完全是因为对中医生这个职业的尊重,为了让人知道中医的深奥。

戒口

佛教五戒之一的不妄语,要求我们不欺骗他人、不在不清楚实际情况的时候胡乱说话,放到职场,也可以加上信息安全的要求。《越绝书》载文种述九术时说:“故曰九者勿患,戒口勿传,以取天下不难,况于吴乎?”文种希望勾践秘而不宣,以免人多口杂,泄露机密。每个人都有自己的岗位、职责,我们要做的是做好自己的事情,不对不属于自己工作范围内的事情评价、传播,不在背后说同事的坏话。作为一名技术人员,如果不能做到戒口、静心、专心,那我觉得你应该尽早转行,你不适合,也绝不会成为一名技术大拿。

气场

一位职业的工作者,他身上有一种称为气场的东西存在。人的气场是看不见的,但这种力量是巨大的,就像万有引力一样,我们每个人身上的这种气场无时无刻不在影响你的人生。这种气场的行程与你的观念、信仰、环境、朋友、呼吸、事物、欲望、静息与睡眠相关。一个人的气质很好,外表精神、有修养、有道德,这个人的气场就好,就会吸引好的事,吸引好的运气。每个人都会遇到各种各样的苦难,但是我坚信,你若顽强到底,一切皆有可能。

教养

看不见的教养很难。在乌合之众中谁能保持优雅和教养?在群体无意识中谁能保持清醒和判断?更难的是那些“慎独”的教养。日本有一种文化,叫作“不给别人添麻烦”的文化,我们每个人在做事之前都应该考虑是否自己的行为会给别人造成麻烦。教养不是道德规范,也不是小学生行为准则,其实也并不跟文化程度、社会发展、经济水平挂钩,它更是一种体谅,体谅别人的不容易,体谅别人的处境和习惯。对于教养,我个人的理解是,谦逊是一种教养,自尊更是。

心态

尼克·胡哲说过,人们经常埋怨什么也做不来,但如果我们只记挂着想拥有或欠缺的东西,而不去珍惜所拥有的,那根本改变不了问题!真正改变命运的,并不是我们的机遇,而是我们的态度。

一个人的心态很是重要,心量小的人,芝麻大小的事情也能在心里翻江倒海。心量大的人,即使在危机面前也能镇静自若。同样一件事情,掀起的波澜大小却因人而异。有一句话很好,用于技术人员我觉得尤其合适,“想要成为一棵大树,就不要去和草争”。

一个人的成就,不得以金钱衡量,而是一生中,你善待过多少人,有多少人怀念你。成功并非单指事业,无论是爱好或职业上的成功都只是成就。成功应该是多元化的,如人的一生包含了很多追求一样,而非单一指向。然后,无论你多有成就,真正的成功,就是陪伴家人。所有的情感都是需要陪伴的,这些陪伴成为一个个美好的回忆,这些都是整个家庭最宝贵、最重要的财富,这些远远超越物质的重要性。在中国,因为价值观相对比较单一,社会显得很浮躁、很物质,所以大多以物质的追求为主,越多越好,内心也想过美好的生活。但当你的心完全趋向金钱的时候,很多美好的东西就会自动屏蔽了,不会出现在生活中。别让忙碌空白了回忆。

此外,作为一名技术人员,我觉得,职业生涯中可能很多次需要面对工作的变换、角色的变化,有很多知识需要学习,所以,我们应该把“归零”当成一种生活的新常态。

劝学

我觉得有一句话总结得特别好,“能干工作、干好工作是职场生存的基本保障”。

荀子是儒家八派中一派的创始人,其思想学说以儒家为本,兼采道、法、名、墨诸家之长。荀子在他的著作《劝学》一文中这样写道,“君子曰:学不可以已。青,取之于蓝,而青于蓝;冰,水为之,而寒于水。”这段文字大体表达了学习是不可以停止的,君子广泛学习并且每天反省自己,就会明白道理,行为上也不会有什么过错。

原浙江工业大学浙商创新发展研究院院长程惠芳认为全球成功的科技型企业,无论是微软的比尔·盖茨,还是苹果的乔布斯,Facebook的扎克伯格,无一不是技术专家,创新型企业必须由这样的企业家带队,懂技术,就会站在前沿。对于大型科技企业而言,光懂技术不够,还要懂市场。

诸葛亮在给他的儿子写的著名的《诫子书》中指出,宁静才能够修养身心,静思反省。不能够静下来,则不可以有效地计划未来,而且学习的首要条件,就是有宁静的环境。审慎理财,量入为出,不但可以摆脱负债的困扰,更可以过着简朴的生活,不会成为物质的奴隶。要计划人生,不要事事讲求名利,才能够了解自己的志向,要静下来,才能够细心计划将来。学习需要专注,平静心境才能事半功倍。学习的过程中,决心和毅力非常重要,因为缺乏了意志力,就会半途而废。拖延就不能够快速地掌握要点。时光飞逝,意志力也会随着时间消磨。

归属感

每个足球队有11位球员在球场上比赛,估计最不引人注目的应该是守门员了吧,他要忍受着大多数时间的无聊,还要保持着警惕。当危机发生时,很有可能还要一个人战斗,需要勇敢地面对对方前锋,唯一的目标是,绝对不让你攻破球门。我们很多时候可能也是如此,苦苦奋斗,当解决了某个问题,或是帮助公司拿到某个招标,我们都会感到自豪感、成就感,这就是归属感,对于技术领域的归属感。

最后,自我介绍一下,我叫周明耀,研究生学历,12年工作经验,IBM开发者论坛专家作者。我是一名IT技术狂热爱好者,一名业余历史研究人员,一名顽强到底的工程师。我推崇技术创新、思维创新,对于新技术非常热爱。

感谢我的家人,和谐的家庭帮助我完成了这本书,我的妻子,她美丽、细心、博学、偶尔不那么温柔,但是我很爱她。我的小顽子,她天生的性格很像我,希望她能够踏踏实实做人,保持创新精神,平平安安、健健康康地生活下去。感谢我妻子父母、我的父母,他们帮我照顾小孩,我才有时间编写此书。感谢浙江省特级教师、杭州高级化学老师郑克良老师,郑老师的一句“永远不要放弃”,推动着我多年的发展。感谢数学老师张老师在公开场合对我智商的褒奖,第一次收获这样的赞赏,对我这样性格的孩子是多么的重要,谢谢。感谢王芳同学,因为你的插画天赋,让这本书的内容更加丰富、可读,不要忽视了自己的才华,你很有天赋。

我相信这本书不是终点,它是麦克叔叔此生一系列技术书籍的开端,下一本书籍见。第1章性能调优策略概述

2011年1月,新加坡飞往杭州的航班。飞行持续时间很长,大约6个小时,坐在四周的人很快熟悉了,互相攀谈起来。有一位小姑娘,十六七岁的模样,长得很漂亮,默默地坐在座位上。热心的阿姨和她攀谈,问起她的情况,她带着疲倦自我介绍起来,“我在新加坡念初三,那所学校一点都不好,我在成都是最好的初中毕业的,也考上了成都最好的高中,但是,我的父母,他们一定要我来新加坡复读初三,让我考新加坡的高中,我一点都不喜欢这里,这里的同学看不起我们这些大陆学生,经常上课找大陆来的老师麻烦,经常辱骂我们,我烦透了!!!”对,这不是自我介绍,这是一个人接近奔溃边缘的歇斯底里。也就是在当时,我做出了决定,我绝不会让我的女儿这样远离我,一个人在很年幼的时候就必须独立面对生活的困难,绝不。无论她的父母出于什么原因让她去国外念书,我所看到的,是让一个不适合承受压力的人承担了巨大的压力,这就是本书的编写原因。在这本书里我想要和大家讨论的话题是基于Java语言的性能优化,我们不能随意地给出性能优化方案,就像随意指派由那位小姑娘来完成全家的未来方向一样。我们必须经过严密的研究、测试及验证,明确造成性能瓶颈真正的原因后才能开始着手,盲目地行动只会造成不必要的损失。当然,如果系统架构设计得很好,就可以在很大程度上避免类似事情发生,这不是本书的主要讨论范围。

本章主要介绍和解决以下问题,这些也是全书的基础:

■ 为什么需要调优,这是您阅读本书的依据,只为需要调优而调优。

■ 了解程序性能的各项指标,包括物理机器性能、程序性能。

■ 性能调优分类方法,包括调优方向、调优方法、调优层次。1.1为什么需要调优

注意,这一节会提到许多技术名词,本着让Java初学者看懂本书的目的,笔者尽量第一时间做出注释,如有遗漏读者可以阅读后续章节,均有详细介绍。

经历了多年的发展,Java已由一门单纯的计算机编程语言,逐渐演变为一套强大的技术体系平台。根据不同的技术规范,Java设计者们将Java划分为3种结构独立但却又彼此依赖的技术体系分支,分别是Java SE、Java EE和Java ME[1],其中Java EE被广泛使用在企业级领域,除了包括Java API组件外,还扩充有Web组件、事务组件、分布式组件、EJB组件、消息组件等,并持续发展到现在。综合Java EE的这些技术,开发人员可以构建出一个具备高性能、结构严谨的企业级应用,并且Java EE也是用于构建SOA架构的首选平台。

Java的持续发展要感谢Google,正是Google将Java作为Android操作系统的应用层编程语言,使得Java可以在PC时代、移动互联网时代都得到迅猛发展,可以用于手持移动设备、嵌入式设备、个人电脑、高性能的集群服务器或大型机。

随着互联网业务的不断拓展、繁荣,越来越多的系统架构开始参照互联网企业的系统架构方式。无论是互联网、物联网,还是传统行业的软件设计,笔者认为,任何技术都离不开对业务需求的支撑[2],所以开始展开研究程序性能问题之前,我们需要先了解系统业务逻辑。

铁道部的 12306[3]网站一直被全国人民所诟病,它确实存在一些问题,但是这些看似简单的问题,其背后牵扯着复杂的系统架构设计。这些设计最终是为业务需求服务的,即12306的职责是为所有旅客的需求服务的,而程序员设计的程序又是为12306服务的,所有的用户体验归到最终就是服务意识。我们来看一下 12306 的业务,12306 需要支持海量并发查询,即海量用户同时查时间、查车次、查座位、查铺位。此外,对应的下单过程也就会伴随着海量并发的数据库操作。据说,淘宝在双十一期间也只有几百万用户[4],而春运期间抢购火车票是全国人民的统一活动,瞬时访问数量有千万级别甚至是亿级别的。据说12306的高峰访问是10亿PV[5],这些访问主要集中在早8点到10点,每秒PV在高峰时上千万[6]。

再来看看其他的业务系统。奥运会期间的奥运票务系统采用抽奖的方式,这样的业务设计让系统不存在先来先得抢购需求,由于是事后抽奖,因此事前只负责收集信息,所以不需要保证数据的一致性,这也就没有高强度并发锁[7]的需求,很容易通过水平扩展方式克服性能瓶颈。B2C网站一般实时性要求不高,比如下单,用户提交订单后,订单并不是马上被处理的,而是等待一定时间后,用户才会收到订单是否确认的通知,这样就确保了数据不需要立即被处理,没有了数据高并发同步的需求。也就是说,在高并发要求下的数据一致性是通常情况下的性能瓶颈点,也是通常意义上的技术难点之一。

前面提起过,高并发情况下的数据高度实时一致性需求是很难实现的。对于一个网站来说,并发浏览网页造成的高负载较容易处理,高并发的查询负载也可以处理,但是实时下单是最难处理的,因为下单需要访问当前的库存量,对于12306网站来说,库存量就是指火车票的库存,由于这是一个全国联网系统,所以可以预见库存量保持数据一致性的难度。据说苹果 CEO 库克[8]正是因为处理好了库存问题才得以继任乔帮主[9]的宝座。目前来看,很多 B2C[10]网站的下单都是通过异步方式来实现的,这样的做法可以避免数据高度一致性要求。

淘宝模式相较于传统B2C网站有一个优势,即它不需要查询库存。B2C网站拥有自己的仓库,每次下单前,都需要查找距离客户最近的仓库是否有库存,这样的计算量累计后会很大。比如,你在上海买一本书,如果上海附近的仓库没货,我们需要先计算哪个仓库既离上海最近又有这本书。淘宝网站由于本身商业模式的原因,它不需要去实时检查库存,反而对于性能扩展较为容易。

的确我们可以通过Nginx[11]来搞定每秒10万的静态请求,只要有足够的网络带宽、磁盘I/O,服务器的并发计算能力够强,可以很容易地处理10万的并发连接。但是如果我们引入了大量的业务逻辑,那就不是单纯的访问问题了,该解决方案也就成了浮云。

除了业务需求、程序运行方式之外,程序设计本身需要考虑基础编程技术、系统架构、网络技术、操作系统、硬件服务器等诸多因素。计算机专家在问题求解时非常重视表达式简洁性的价值。UNIX的先驱者Ken Thompson[12]曾经说过非常著名的一句话:“丢弃1000行代码的那一天是我最有成效的一天之一。”这对于任何一个需要持续支持和维护的软件项目来说,都是一个当之无愧的目标。早期的Lisp[13]贡献者Paul Graham[14]甚至将语言的简洁性等同为语言的能力。这种对能力的认识让我们把编写紧凑、简洁的代码作为许多现代软件项目选择语言的首要标准。

任何程序都可以通过重构代码方式去除多余的代码或无用的占位符,例如空格,删除空格后会让代码变得更加简短。不过某些语言天生就善于表达,也就特别适合于简短程序的编写。APL语言的设计理念是利用特殊的图形符号让程序员用很少量的代码就可以编写功能强大的程序。这类程序如果实现得当,可以很好地映射成标准的数学表达式。简洁的语言在快速创建小脚本时非常高效,特别是在目的不会被简洁所掩盖的简洁明确的问题域中。

相比于其他程序设计语言,Java 语言的冗长已经名声在外,主要原因是由于程序开发社区中所形成的惯例。在完成任务时,很多情况下要更大程度地考虑描述性和控制能力。例如,长期来看,长变量名会让大型代码库的可读性和可维护性更强。描述性的类名通常会映射为文件名,在向已有系统中增加新功能时会显得很清晰。如果能够一直坚持下去,描述性名称可以极大简化用于表明应用中某一特定的功能的文本搜索。实践证明,这些定义方式让Java在大型复杂代码库的大规模实现中取得了极大的成功。

相对于传统的32位虚拟机,64位虚拟机所具备的最大优势就是可以访问大内存。32位虚拟机最大可用内存空间被限定在了4GB,并且Java堆区的大小配置存在最大限制,如果是在Windows平台下最大只能设置到1.5GB,而在Linux平台下最大也只能设置到2GB~3GB。也就是说,Java堆区的内存大小设置还需要依赖于具体的操作系统平台。

既然32位虚拟机无法满足大内存消耗的应用场景,那么64位虚拟机的出现则是顺理成章的。64位虚拟机之所以能够访问大内存,是因为其采用了64位的指针架构,这也是寻址访问大内存的关键要素。

在JDK1.6 Update14版本之前,64位虚拟机的综合性能表现实际上是不如32位虚拟机的,这主要是因为OOPS(Ordinary Object Pointers,普通对象指针)从32位膨胀到64位后,CPU Cache Line中的可用OOPS变少,这样一来就会直接影响并降低CPU的缓存使用率,这就是64位虚拟机在性能上之所以落后于32位虚拟机的主要原因。其次,由于部署在64位虚拟机上的性能都需要用到大内存,尤其是互联网项目,经常需要使用多达几十乃至几百GB的内存,这对于传统的32位虚拟机将无法承载,只能依靠64位虚拟机去支撑。但是管理这么大的内存开销对于GC(Garbage Collection)来说将会是一场非常严峻的考验,甚至很有可能会导致GC在执行内存回收期间消耗更长的时间,同时也意味着工作线程的等待时间将会延长。如今随着64位虚拟机的逐渐成熟,指针压缩将会通过对齐补白等操作将64位指针压缩为32位,以此改善CPU缓存使用率达到提升64位虚拟机运行性能的目的。

对于小型项目来说,简洁性则更受青睐,某些语言非常适于短脚本编写或者在命令提示符下的交互式探索编程。Java作为通用性语言,则更适用于编写跨平台的工具。在这种情况下,“冗长Java”的使用并不一定能够带来额外的价值。虽然在变量命名等方面,代码风格可以改变,不过从历史情况来看,在一些基本的层面上,与其他语言相比,完成同样的任务,Java语言仍需更多的字符。为了应对这些限制,Java语言一直在不断地更新,尝试包含一些通常称为“语法糖”的功能。用这些习语可以实现用更少的字符表示相同功能的目标,与其对应的更加冗长的配对物相比,这些习语更受程序开发社区的欢迎,也会被社区作为通用用法快速地采用。

现代CPU架构将多核、多硬件执行线程技术推向前台,这意味着我们可以利用更多的CPU资源做更多的工作。然而,要利用好这些额外的CPU资源,运行于其上的程序必须要能够支持并行工作这个需求。通俗点讲,这些程序需要按照多线程的方式构造或设计才能充分地利用额外的硬件线程。

最近这几年,服务器端网络使用的基础通信技术并没有取得太大的进步。服务器端大多数在绝不允许服务中断的关键任务环境中,新技术很难渗透,也很难植根于这样的环境。但正因如此,服务器端的多余部分才得以被剔除,逐渐地形成了非常精简单纯的风格。网络的基础技术可以说已经成型了,然而在网络上运行的网络设备和服务器的技术仍然踩着现在进行时的节奏在持续不断地爆发性发展,由此出现了虚拟技术和网络存储技术等基于网络的创新技术。如今,它们已经在系统中不可或缺。随着这些技术的发展,人们追求的网络形态和网络设计的方式也在时刻发生着变化,基础架构工程师和服务器工程师必须能灵活应对这些变化才行。对应地,软件设计程序员也需要有针对性地做出应对措施。

虚拟化技术是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来。此举打破了实体结构间的不可切割的障碍,使用户可以用比原本形态更好的方式来应用这些资源。虚拟化资源一般包括计算能力和资料存储介质,这些资源的虚拟部分不受现有资源的架设方式、地域或物理形态所限制。虚拟化技术在带来成本节省和运维便利的同时,也带来了一些新的挑战,对架构师、运维人员、程序员等角色提出了新的要求。在解决了物理设施的虚拟化问题之后,我们的目光可能就会转移到应用程序本身上来,因为这才是真正为用户体现支付价值的关键所在。特别需要注意的是,部署在虚拟化环境上的Java应用与物理环境上的应用存在明显的区别。

综上所述,性能优化本身对于程序性能是至关重要的,同时性能优化也是一门综合性课程,虽然本书针对的是Java程序的性能优化,但是依然需要考虑综合性因素。作者认为,随着IT技术的蓬勃、快速发展,性能调优已经不单纯是代码级别的调优,它是一个对综合性知识的深入理解需求,我们只有结合多方面的技术才能真正找到合理的解决方案。这也是本书除了深入介绍Java程序调优、JVM调优等之外,坚持引入服务器、网络、云计算、虚拟化等多维技术点的原因。1.2性能优化的参考因素

系统性能优化,或者称之为程序性能优化,它存在的理由有很多。举一个政治上的例子,意大利由于天生的漫长海岸线,所以它一直以来都是难民逃亡欧洲的跳板。意大利政府一直都受困于这个难民潮问题,不管阻拦还是放行难民,这些举措都会受到北欧国家的指责。后来意大利政府从很多难民口中知道他们其实想去德国,只不过路过这里,所以干脆就来一招狠的,让难民填写意愿国家,只要填写了就直接大巴送到国境线上去。这样一来,德国吃紧了,一下子很多难民涌入,就造成了整个国家的运转问题。就好似计算机程序的性能问题,面对海量数据或者任务时,无论如何你都会碰到性能压力,唯一的选择是你会把这个压力放在哪一层或者哪一个位置来应对,以及采取什么应对措施。下面开始具体解释这些造成性能问题的因素点。1.2.1 传统计算机体系的分歧

如果说图灵[15]奠定的是计算机的理论基础,那么冯·诺依曼[16]则是将图灵的理论物化为实际的物理实体,成为了计算机体系结构的奠基者。从第一台冯·诺依曼计算机诞生到今天已经过去了将近70年,计算机的技术与性能也都发生了巨大的变化,但整个主流体系结构依然是冯·诺依曼结构。

冯·诺依曼体系结构是采用二进制形式存储数据,硬件由5个部分组成,分别是运算器、控制器、存储器、输入设备和输出设备。同时提出了“存储程序”原理,即使用同一个存储器,然后经由同一个总线传输,程序和数据统一存储,同时在程序控制下自动工作。特别要指出,它的程序指令存储器和数据存储器是合并在一起的,程序指令存储地址和数据存储地址指向同一个存储器的不同物理位置。因为程序指令和数据都用二进制码表示,且程序指令和被操作数据的地址又密切相关,所以几十年前选择这样的结构是合理的。

但是,随着对计算机处理速度要求的提高和对需要处理数据的种类、量级的需求不断增大,这种指令和数据共用一个总线的结构,使得信息流的传输成为限制计算机性能的一个瓶颈,制约了数据处理速度的提高。由此,体现出了冯·诺依曼体系结构的局限性,如下面4点:(1)目前CPU的处理速度和内存容量的增长速率要远大于两者之间的流量,将大量数值从内存搬入搬出的操作占用了CPU大部分的执行时间,也造成了总线的瓶颈;(2)程序指令的执行顺序是串行的,由程序计数器控制,这样使得即使有关数据已经准备好了,也必须遵循逐条执行指令序列,这样的设计影响了系统运行的速度;(3)存储器是线性编址,按顺序排列的地址访问,这样设计有利于存储和执行机器语言,适用于数值计算。高级语言的存储采用的是一组有名字的变量,是按名字调用变量而不是按地址访问,且高级语言中的每个操作对于任何数据类型都是通用的,不管采用何种数据结构,多维数组[17]、二叉树[18]还是图,最终在存储器上都必须转换成一维的线性存储模型进行存储。这些因素都导致了机器语言和高级语言之间存在很大的语义差距,这些语义差距之间的映射大部分都要由编译程序来完成,在很大程度上增加了编译程序的工作量;(4)冯·诺依曼体系结构计算机是为逻辑和数值运算而诞生的,它以CPU为中心,I/O设备与存储器间的数据传送都要经过运算器,在数值处理方面已经达到很高的速度和精度,但对非数值数据的处理效率比较低,需要在体系结构方面有革命性突破。

科学家们一直在努力突破传统的冯·诺依曼体系结构框架,对冯·诺依曼计算机进行改良,主要体现在以下3点:(1)将传统计算机只有一个处理器串行执行改成多个处理器并行执行,依靠时间上的重叠来提高处理效率,形成支持多指令流、多数据流的并行算法结构;(2)改变传统计算机控制流驱动的工作方式,设计数据流驱动的工作方式,只要数据准备好了,就可以并行执行相关指令;(3)跳出采用电信号二进制范畴,选取其他物质作为执行部件和信息载体,如光子、量子或生物分子等。

近几年,在计算机体系结构研究方面也已经有了重大进展,越来越多的非冯式计算机相继出现,如光子计算机、量子计算机、神经计算机以及DNA计算机等。

光子计算机(Photonic Computer)是一种采用光信号作为物质介质和信息载体,依靠激光束进入反射镜和透镜组成的阵列进行数值运算、逻辑操作和信息的存储和处理。它可以实现对复杂度高、计算量大、实时性强的任务的高效、并行处理,比普通电子计算机快1000倍,在图像处理、模式识别和人工智能方面有着非常巨大的应用前景。

神织神能成神经计算经元为处理、自学习、经计算机可决策。它的,适用于图机(Neural信息的基本自适应及自以模拟人的左脑由100形图像识别Computer)是单元,将模仿修复功能,可左脑和右脑,万个神经元组。这将有可能一种可以并大脑神经记以模仿人脑能识别语言成,用于存成为人工智行处理多种忆的信息存的判断能力文字和图形储文字和语能硬件发展的数据功能的神放在神经元上和适应能力。图像,能控制法规则,右脑主攻方向。经网络计算。神经网络美国科学家机器人行为由1万多个机,它以具有自组研究出的,进行智神经元组

及子比它的量子计算处理量子信、束缚离子特位不同,可以同时表硬件条件。机(Quantum息的物理装和原子等制对量子位操作示多种状态Computer)置。量子计算成的量子位,1次等同于。这些都为新是一种遵循机本身的特创造出了经对经典位操的算法实现量子力学规性,扩充了典条件下不作2次,因为提供了条件律进行高速数逻辑和数学理可能存在的新量子不像半,也为人工智学和逻辑运论,通过核的逻辑门。导体只能记录能的发展提算、存储自旋、光与经典的0和1,供了可能

可计这这新我们不可能在今后很算机更快速样复杂的结样才能使计的信息时代否认,冯·长的一段时、更高效、构,我们需要算机有质的。诺依曼计算机期里还将为人更方便的使用突破现有的飞跃。相信未以其技术成类的工作和要求,为了体系结构框来随着非冯熟、价格低生活发挥着让计算机能架并寻求新的式计算机的廉、软件丰富重要作用。但够模拟人脑神物质介质作商业化推进,和大众的使是,为了满经元和脑电为计算机的信我们将会迎用习惯,足人们对信号脉冲息载体,来一个崭1.2.2 导致系统瓶颈的计算资源

成根据应用为系统瓶颈程序的不同的计算资源特点,任何计如图1-1所示算机部件都,包括CPU有可能成为、内存、磁盘系统瓶颈爆发、网络、数点。其中,据库等。最有可能

图1-1 系统瓶颈原因

■ CPU对 CP资源需:对计算资源U 的争夺将求较大。要求较高的导致性能问题应用,由于其。如视频分长时间、不析、科学计间断地大量算、3D 渲染占用 CPU 资等应用场景源,这样都对 CPU

■ 内存:非应用一般来说,程序进行了只要应用程序高频率的内设计合理,存交换和扫描内存在读写,但这些情速度上不太可况比较少见。能成为性能内存制约系瓶颈,除统性能的最可能发生的情况是内存大小不足,这种情况下会导致应用程序无法创建对象,更严重甚至导致操作系统无法正常运行。与磁盘相比,内存的大小较小,这意味着应用软件只能尽可能将常用的核心数据读入内存,大量的数据还是需要存放在磁盘上,这个特性在一定程度上降低了系统性能。

■ 磁盘I/O:磁盘I/O读写速度比内存慢很多。随着硬盘技术的不断发展,SSD[19]固态硬盘的引入确实已经加快了磁盘的读写速度,但是性价比不高,速度也还是慢于内存。因为读写性能原因,程序在运行过程中,如果需要等待磁盘I/O完成,那么低效的I/O操作会拖累整个系统。

■ 网络传送:对网络数据进行读写的情况与磁盘I/O类似。由于网络环境的不确定性,尤其是对互联网上数据的读写,网络操作的速度可能比本地磁盘I/O更慢。因此,如果不加特殊处理,也极有可能成为系统瓶颈。

■ 数据库:大部分应用程序都离不开数据库,无论是关系型数据库,还是列式数据库,它们都存在连接数量、读写速度、数据合并等制约因素,而针对海量数据的读、写操作则可能更加耗费时间。应用程序可能需要等待数据库操作完成或者等待返回检索请求需要的结果集,即这类同步操作容易成为系统瓶颈。我们可以通过一些异步操作、多数据中心等方式来解决局部问题,但是无法解决所有由于数据库导致的问题。总的来说,数据库是最容易导致应用程序性能瓶颈的原因之一。

■ 锁竞争:对高并发程序来说,如果存在激烈的锁竞争,无疑是对性能极大的打击。锁竞争将会明显增加线程上下文切换的开销,而且这些开销都是与应用需求无关的系统开销,导致大量占用宝贵的 CPU 资源,却不带来任何好处。总的来说,锁竞争问题对于程序员来说最难处理,因为处理这方面问题需要大量的操作系统、编程语言并发知识及经验。

■ 异常:对Java应用来说,异常的捕获和处理是非常消耗资源的。如果程序高频率地进行异常处理,则整体性能便会有明显下降。高级编程语言一般都提供异常捕获及处理机制,相对来说程序员比较容易掌握。1.2.3 程序性能衡量指标

如果抛开所有的内部技术因素,我们只看应用程序的性能指标,那么一般来说,程序的性能大体可以通过以下几个方面来衡量。

■ 响应时间:系统对用户行为或者事件做出响应的时间。响应时间越短,性能一定越好,所以我们在系统设计过程中应该尽量采用异步处理方式,让用户能够尽快收到回执,这样用户体验会较好。

■ 启动时间:应用系统从运行到可以正常处理业务所需要花费的时间,对于用户来说,肯定是越快启动越好,所以我们在系统设计过程中应该尽量采用异步加载数据的方式启动应用程序,避免等待所有数据加载完毕后才启动。

■ 执行时间:一段代码从开始运行到运行结束,所使用的时间称为执行时间。对于执行时间,有些时候可能无法减少全局化的时间,但是可以通过把业务逻辑切分到多段连续的程序段中,让用户感觉执行时间减短了。

■ 执行速度:程序的反应是否迅速,响应时间是否足够短。该指标与响应时间、执行时间是相关联的。

■ 计算资源分配:计算资源,包括 CPU、内存、磁盘等,如果其中的任何一项分配不合理,可能会导致整个系统始终处于计算资源紧张的情况下,这样对于整个系统的性能影响一定是毁灭性的。

■ 内存分配:内存分配是否合理,是否过多地消耗内存或者存在泄漏,JVM性能也与内存分配有一定关系。

■ 磁盘吞吐量:描述I/O的使用情况。IOPS(Input/Output Per Second)即每秒的输入输出量(或读写次数),是衡量磁盘性能的主要指标之一。IOPS是指单位时间内系统能处理的I/O请求数量,I/O请求通常为读或写数据操作请求。随机读写频繁的应用,如OLTP(Online Transaction Processing),IOPS是关键衡量指标。另一个重要指标是数据吞吐量(Throughput),指单位时间内可以成功传输的数据数量。对于大量顺序读写的应用,如 VOD(Video On Demand),则更关注吞吐量指标。每秒I/O吞吐量=IOPS×平均I/O SIZE。从公式可以看出,I/O SIZE越大,IOPS越高,那么每秒I/O的吞吐量就越高。因此,我们会认为IOPS和吞吐量的数值越高越好。实际上,对于一个磁盘来讲,这两个参数均有其最大值,而且这两个参数也存在着一定的关系。

■ 网络吞吐量:描述网络的使用情况。网络中的数据由一个个数据包组成,防火墙对每个数据包的处理要耗费资源。吞吐量是指在没有帧丢失的情况下,设备能够接受的最大速率。其测试方法是:在测试中以一定速率发送一定数量的帧,并计算待测设备传输的帧,如果发送的帧与接收的帧数量相等,那么就将发送速率提高并重新测试;如果接收的帧少于发送的帧则降低发送速率重新测试,直至得出最终结果。吞吐量测试结果以“比特/秒”或“字节/秒”表示。

■ 负载承受能力:当系统压力上升时,系统的执行速度、响应时间的上升曲线是否平缓。负载承受能力与计算资源、内存、磁盘、网络等多方面因素都有关联。1.2.4 性能优化目标

与前面章节不同,我们这里抛开所有的外部因素,只单纯分析Java程序本身,那么大多数Java程序性能优化目标都可以归纳到下面几类。

■ 编写更有效率的代码:应用程序的每CPU指令时钟周期(Clocks Per Instruction,CPI)指的是执行一条CPU指令所消耗的CPU时钟数。编译器将Java源代码编译成字节码,而CPI正是被用来衡量字节码效率的指标。由于应用程序的最终执行基于字节码,所以调整应用程序、Java虚拟机或操作系统,缩短应用程序的CPI,让编译器生成更优的指令等,这些举措都有助于提升应用程序的性能。执行路径长度与CPI之间有微妙的差别,执行路径长度与应用程序的算法选择关系密切,而CPI与编译器生成更有效的代码有关。前者着眼于通过选择合理的算法生成最短的 CPU 指令序列,后者着眼于让编译器生成最高效的代码,减少每条CPU指令上消耗的CPU时钟周期数。如果CPU时钟周期被用来执行操作系统指令或内核代码,那么这部分时钟周期就无法用于执行应用程序。因此,改善应用程序性能的策略之一是减少消耗在系统或内核 CPU 上的时钟周期数。注意,该策略不适用于在操作系统或内核态上消耗时间极少的应用程序。

■ 使用更高效的算法:对应用程序进行性能调优所获得的最大收益来自于算法效率的提高。高效的算法主要可以在业务逻辑处理层面起到重要的作用,可以让应用程序使用更少的CPU指令、更短的执行路径来实现程序功能。通常情况下,拥有更短执行路径的应用程序运行得更快。从应用程序来看,使用更好的数据结构或者改进算法可以构造出更短的执行路径,很多应用程序的性能问题都源于使用了不合适的数据结构。因此,使用恰当的数据结构及算法是提升程序性能最有效的方法。在性能分析过程中,我们要充分注意程序使用的数据结构及算法,尽可能采用更优的方式,这样做才能最大限度地提高程序性能。

■ 减少锁竞争:对共享资源的竞争容易限制应用程序的可扩展性。频繁进行锁竞争的应用程序的性能是无法随线程数和 CPU 数增加而提高的,因此,减少锁竞争的频率、缩短锁持有的时间都能够有效地优化应用程序。1.2.5 性能优化策略

上面各子章节对性能调优可能出现的位置、性能衡量指标、优化目标等进行了一些总结,下面列举几点常见的性能优策略。

■ 用空间换时间。该策略属于系统架构层面的优化。我们知道,各种缓存机制,从 CPU L1/L2/RAM到硬盘,都可以通过空间换时间的策略。这类策略基本上是通过采用把计算的过程一步一步地保存或者缓存等方式,避免重复计算的发生。具体的方式可以采用数据缓冲、CDN等。类似的策略还表现为诸如冗余数据,比如数据镜像、负载均衡等。

■ 用时间换空间。该策略也属于系统架构层面的优化。有时候使用少量的空间,可能性能会更好。比如网络传输,如果有一些压缩数据的算法,例如Huffman编码压缩算法[20],该算法本身运行过程其实很耗时,但是因为整体来看性能瓶颈在网络传输部分,所以用时间来换空间反而能省时间。

■ 简化代码。该策略属于基础技术层面的优化。最高效的程序就是不执行任何代码的程序,所以,大多数情况下我们可以认为代码越少性能就越高。关于代码级优化的技术,大学的教科书中有很多示例了。比如,减少循环的层数、减少递归、在循环中少声明变量、少做分配和释放内存的操作、尽量把循环体内的表达式抽到循环外、条件表达式中的多个条件判断的次序、尽量在程序启动时把一些东西准备好、注意函数调用的开销(栈上开销)、注意面向对象语言中临时对象的开销、小心使用异常(不要用异常来检查一些可接受可忽略并经常发生的错误),等。这类知识需要我们非常了解编程语言和常用的语言自带资源库。从同步问能并不等待,码上的根本上来讲题,如果单好,当大量具体我们会简化。,不一定越少纯采用同步线程同时访在第5章详的代码越好锁(Synchro问代码块时,细介绍。所以,我们需要nized)的方它会生成大,我们还是了解Java本地式,代码确实量的锁,导致要有一定技术库的实现原很少,但是系统内部代基础之后才理,例如实际的性码块互相能做到代

■ 并行处进程、的操作出多进型任务采用资区域,理。该策略多线程的方系统调度和程多线程的测试,不一源隔离技术这样可以保属于一种综式编写代码切换开销所优势。实际定核数多就一对 CPU 核上证更多的并合性策略。试,那么计算密导致的,这类应用过程中,定是最佳选的计算资源行程序同步进想,如果 C集型需求的型优化策略我们会针对择,也有些进行切分,行,这也与PU 只有一个应用程序反而只能依靠多核不同 CPU 进情况需要单核把应用程序线整个系统的核,你要是会更慢,这CPU 才能行大量高并能力强的C程部署到不架构、内存共还采用多是由巨大真正体现发、密集PU,然后同的隔离享策略、任务调需要我扩展的度机制等多们的程序拥程序无法进方面因素相有扩展性,不行并行处理关联。并行处能水平或垂。理直

化计找以综上所述上来看,如或程序代码到那 20%的较为容易地,我们把经典图1-2所示,消耗了 80%缺陷设计或优化、解决的二八原则统计学认为的系统性能。者劣质代码,80%的性能问[21]移植到性能,20%的系统如果我们可那么我们就题。优设以可图1-2 二八原则1.3性能调优分类方法

这性能调优三个方面的方法一般来问题,沙漏说可以分为基才能正常工作础技术、架。构、层次这三个方面,如图 1-3 所示,解决了图1-3 性能调优分类方法1.3.1 业务方面

1.3.1.1 商业事务

商业事务是真实用户体验的直观反映,它们在抓取用户与应用交互时用户体验到的实时性能数据。测量商业事务的性能,需要抓取一件商业事务整体的响应时间及其各个组件的响应时间。这些响应时间再与满足业务需求的基准进行比较,从而决定应用是否正常。

商业事务通过其入口进行辨别,即它是用户与你的业务进行互动的入口。这类互动包括,一个网页请求、一个网页服务调用、消息队列中的一条消息等。当然,你也可以基于一个URL参数为同样的网页请求定义多个入口,或基于一个服务调用的内容定义多个入口点。关键在于,商业交易必须与你的业务流程相关联,比如说中国移动的空中缴费业务对应到系统中是多个原子服务,我们就应该将这几个原子服务通过相应的关联聚合成一个空中缴费业务来进行监控。

每个商业交易的性能会与其基准进行比较,判定其是否正常。譬如,如果某个商业事务的响应时间大于你设定的阈值,我们便判定其运行异常。

总而言之,商业事务最能反映用户体验,因此它们也是最重要的抓取维度。

1.3.1.2 外部服务

外部服务的形式多种多样,包括从属的网页服务、遗留系统或数据库等。外部服务是与应用交互的系统。运行在外部服务系统中的代码常常无法控制,但是我们可以控制这些系统的配置,因此了解他们是否运行正常以及何时出错也很重要。此外,我们必须有能力区分问题是出自应用本身,还是出自这些外部服务系统。

从商业事务的角度来说,我们可以辨别并测量这些处于自身应用的外部服务。例如,我们需要配置监控方法从而辨别那些包裹了外部服务调用的方法。但是对于常见的协议,诸如HTTP和JDBC,外部服务可以自动检测。

商业事务让你对应用的性能有了全局的掌控,帮助你对性能问题进行分类。但是外部服务总能以意想不到的方式极大地影响应用的运行,所以你必须监控它们。

1.3.1.3 应用布局

因为云的出现,现在的应用变得更加灵活,即应用环境可以根据用户需求调节大小。因此,对应用的布局进行检测从而决定实例的数量是否合适是非常重要的。如果你的应用实例太多,你的云主机成本就会增加。但如果你没有足够的实例,商业事务就会受到影响。具体来说,你需要确定是否有应用中的实例负载过大,如果有,你或许想在那个应用中添加实例。从应用的角度查看实例状态很重要,因为单个实例可能由于垃圾回收之类的因素负载过大,但如果应用中大多数实例都负载过大,则该应用可能已经无法支持它接受的访问量。1.3.2 基础技术方面

无论你现在正在专注于前端软件技术,还是后端软件技术,所有的语言都需要使用到编译器,那么一般来说编译器是如何工作的呢?

编译器在开始工作之前,需要知道当前的系统环境,比如标准库在哪里、软件的安装位置在哪里、需要安装哪些组件等。这是因为不同计算机的系统环境不一样,通过指定编译参数,编译器就可以灵活适应环境,编译出各种环境都能运行的机器码。这个确定编译参数的步骤,就叫作“配置”(configure)。源码肯定会用到标准库函数(standard library)和头文件(header)。它们可以存放在系统的任意目录中,编译器实际上没办法自动检测它们的位置,只有通过配置文件才能知道。接下来,就是从配置文件中知道标准库和头文件的位置。一般来说,配置文件会给出一个清单,列出几个具体的目录。等到编译时,编译器就按顺序到这几个目录中,寻找目标。

对于大型项目来说,源码文件之间往往存在依赖关系,编译器需要确定编译的先后顺序。假定A文件依赖于B文件,编译器应该保证做到下面两点。(1)只有在B文件编译完成后,才开始编译A文件。(2)当B文件发生变化时,A文件会被重新编译。

不同的源码文件,可能引用同一个头文件(比如 stdio.h[22])。编译的时候,头文件也必须一起编译。为了节省时间,编译器会在编译源码之前,先编译头文件。这保证了头文件只需编译一次,不必每次用到的时候都重新编译了,Java也是类似的,称为Class文件。预编译完成后,编译器就开始替换掉源码中bash的头文件和宏。预处理之后,编译器就开始生成机器码。对于某些编译器来说,还存在一个中间步骤,会先把源码转为汇编码(assembly),然后再把汇编码转为机器码。机器码生成后连接是在内存中进行的,即编译器在内存中生成了可执行文件。下一步,必须将可执行文件保存到用户事先指定的安装目录。表面上,这一步很简单,就是将可执行文件(连带相关的数据文件)拷贝过去就行了。但是实际上,这一步还必须完成创建目录、保存文件、设置权限等步骤。这整个的保存过程就称为“安装”(Installation)过程。最后我们可以通过生成若干个二进制文件的形式,以开放服务把编译后的程序提供给用户。

1.3.2.1 前端技术

■前端负载均衡

通过 DNS[23]的负载均衡器(一般在路由器上根据路由的负载重定向)可以把用户的访问均匀地分散在多个Web服务器上,这样的做法可以减少Web服务器的请求负载。因为HTTP的请求都是短连接,所以可以通过很简单的负载均衡器来完成这一功能。注意,最好是有CDN网络让用户连接与其最近的服务器(CDN[24]通常伴随着分布式存储[25])。

■减少前端连接数

通过减少访问页面,CSS静态切分JS、图片的方式,把单一用户的连接数降到最低。

■减少网页大小增加带宽

通过减少网页上附带的图片可以大大降低网络带宽压力。

■前端页面静态化

通过静态化一些不常变化的页面和数据,可以通过技术手段让用户直接从内存中读取这些信息,这样可以减少磁盘I/O[26]。

■优化查询

对于查询相同内容的用户,可以通过反向代理方式处理。这样的技术主要用查询结果缓存来实现,第一次查询时需要到数据库获得数据后把数据放到缓存,之后的查询直接访问高速缓存。

■缓存的问题

缓存可以被用来缓存动态页面,也可以被用来缓存查询的数据。很多 NoSQL[27]数据库可以解决下述问题。(1)缓存的更新,也叫缓存和数据库的同步。可以通过缓存失效时间限定、中心统一更新等方式实现;(2)缓存的换页。通过把一些不活跃的数据换出内存可以解决内存不足的问题,常用的换页算法有FIFO、LRU、LFU等算法;(3)缓存的重建和持久化。缓存一旦丢失,我们就需要重建缓存,如果数据量很大,缓存重建的过程会很慢,这会影响生产环境,所以,缓存的持久化也是需要被考虑的。

1.3.2.2 服务端技术

■数据冗余

通过减少表连接的方式可以降低数据冗余,但是它牺牲了数据的一致性,风险比较大。通过NoSQL可以冗余数据。

■数据镜像

几乎所有主流的数据库都支持镜像,数据库的镜像带来的好处之一是可以做负载均衡。把一台数据库的负载均分到多台上,同时又保证了数据一致性。最重要的是,这样还可以有高可用性,起到HA[28]的作用。

数据镜像的数据一致性可能是个复杂的问题,我们可以通过针对单条数据进行数据分区的方式实现该功能。除了传统方式外,我们可以通过现在正火热的Docker[29]来实现数据镜像。

■数据分区

数据镜像不能解决的一个问题就是数据表里的记录太多,导致关系型数据库操作太慢,可以通过对数据进行分区来解决该问题。数据分区有很多种做法,一般来说有下面这几种:(1)把数据把某种逻辑来分类。比如火车票的订票系统可以按各铁路局来分,可按各种车型分,可以按始发站分,可以按目的地分……反正就是把一张表拆成多张有一样的字段但是不同种类的表。这样,这些表就可以存在不同的机器上以达到分担负载的目的。(2)把数据按字段切分。比如把一些不经常改的数据放在一个表里,经常改的数据放在另外多个表里。把一张表变成1对1的关系,这样,你可以减少表的字段个数,同样可以提升一定的性能。另外,字段多会造成一条记录的存储会被放到不同的页表里,这对于读写性能都有问题。但这样一来会有很多复杂的控制。(3)平均分表。因为第一种方法是并不一定平均分均,可能某个种类的数据还是很多。所以,也有采用平均分配的方式,通过主键ID的范围来分表。(4)同一数据分区。这个在上面数据镜像提过。也就是把同一商品的库存值分到不同的服务器上,比如有10000个库存,可以分到10台服务器上,一台上有1000个库存。

这四种分区都有好有坏。最常用的还是第1种。数据一旦分区,你就需要有一个或是多个调度来让你的前端程序知道去哪里找数据。

NoSQL数据库因为它特有的分布式、数据互备概念,所以已经解决数据分区问题。

■后端系统负载均衡

数据分区可以在一定程度上减轻负载,但是还是无法减轻热点数据[30]的负载,需要使用数据镜像来减轻负载。使用数据镜像,则必然要使用负载均衡,我们需要一个任务分配系统,该系统可以监控各个服务器的负载情况,这样的设计有点类似于 Master-Slaves,现在较为流行的大数据Hadoop[31]、Spark[32]等大数据框架都是按照这类方式设计的。

任务分配服务设计上天生有一些难点:

■ 负载情况比较复杂。负载均衡算法设计没有固定规则,我们只有根据生产环境下整体的系统负载能力、单台机器的负载能力、外部等待任务等多方面的情况来设计该算法;

■ 任务分配服务需要任务队列。任务队列可以帮助保持任务排队序列、收集任务执行状态、持久化任务;

■ 任务分配服务需要高可用性的技术。此外,我们还需要注意被持久化的任务队列如何转移到别的服务器上。

■异步

异步采用的是延时处理方式。在技术上来说,就是把各个处理程序做成并行化,这样也就可以水平扩展了。使用异步方式需要考虑几点:(1)被调用方的结果返回,会涉及进程线程间通信的问题;(2)如果程序需要回滚,回滚会有点复杂;(3)异步通常都会伴随多线程多进程,并发的控制也相对麻烦一些;(4)很多异步系统都用消息机制,消息的丢失和乱序也会是比较复杂的问题。

■节流阀

无论是叫节流阀或者流控机制,该技术主要是防止系统被超过自己最大负荷的外部调用拖垮,它属于一种对自身系统的保护机制。使用节流阀技术一般来说是针对一些自己无法控制的系统,比如,和B2C网站对接的银行系统,视频分析领域针对视频的流控措施均属于这类功能。

■批量处理

批量处理的技术是把一堆属于相同类别的信息放在一起请求批量处理的过程,它适用于离线处理模式,即适用于实时性要求不高的需求。网络上的MTU(最大传输单元),以太网是1500字节,光纤可以达到4000多个字节,如果你的一个网络包没有放满这个MTU,那就是在浪费网络带宽,因为网卡的驱动程序只有一块一块地读效率才会高。因此,网络发包时,我们需要收集到足够多的信息后再做网络I/O,这也是一种批量处理的方式。

■垃圾回收技术

从Java发布最早版本开始,一直都保留的核心特性就是垃圾回收,垃圾回收使我们不再需要手动管理内存。当使用完一个对象后,我们只需删除它的引用,然后垃圾回收就会自动释放它。垃圾回收为程序员们减少了分配、释放内存空间等烦琐步骤。

尽管垃圾回收达成了无须手动管理内存的目标,也防止了传统的内存泄露,但是作为代价,垃圾回收过程有时相当笨拙。注意,根据不同的JVM,垃圾回收策略也有所不同。垃圾回收最大的敌人就是传说中的主要(major)或(full)垃圾回收。除了Azul JVM[33],所有的JVM都存在这个问题。

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载