- 浏览: 149344 次
- 性别:
- 来自: 上海
文章分类
- 全部博客 (166)
- Spring (7)
- JavaSE (16)
- web (1)
- Struts2 (2)
- Maven (1)
- 面试 (8)
- Hibernate (4)
- mysql (2)
- WebSerice (2)
- 工作心得 (23)
- 北京 (1)
- 上海 (27)
- groovy (1)
- 生活 (9)
- Linux (12)
- junit (1)
- oracle (5)
- h2db嵌入式数据库 (1)
- redis (14)
- 生活感悟 (1)
- cron (1)
- tomcat (4)
- 设计模式 (7)
- 正则表达式 (1)
- html5 (2)
- 网络编程 (3)
- 微信公众号 (1)
- Java nio (5)
- jdbc (1)
- 框架漏洞 (1)
- 安全 (8)
- 分布式 (2)
- 数据结构 (5)
- xml,json (0)
- xml (1)
- json (1)
- netty (0)
- java8 (1)
- javascript (1)
- 2016计划 (1)
- Java集合源码分析 (1)
- 工作心得,jvm性能调优 (3)
- 转载 (14)
- 产品 (1)
- JVM (10)
最新评论
-
oplife:
都不太难
返利网面试 -
飞翔神话:
谢谢 7 楼 cnrainbing
联想中望面试心得体会 -
飞翔神话:
5 楼 home198979,相比您可能还差点,但是我还是有一 ...
联想中望面试心得体会 -
cnrainbing:
支持小伙子
联想中望面试心得体会 -
jahu:
这简单啊,
联想中望面试心得体会
Java应用程序是运行在JVM上的,但是你对JVM技术了解吗?这篇文章(这个系列的第一部分)讲述了经典Java虚拟机是怎么样工作的,例如:Java一次编写的利弊,跨平台引擎,垃圾回收基础知识,经典的GC算法和编译优化。之后的文章会讲JVM性能优化,包括最新的JVM设计——支持当今高并发Java应用的性能和扩展。
如果你是一个开发人员,你肯定遇到过这样的特殊感觉,你突然灵光一现,所有的思路连接起来了,你能以一个新的视角来回想起你以前的想法。我个人很喜欢学习新知识带来的这种感觉。我已经有过很多次这样的经历了,在我使用JVM技术工作时,特别是使用垃圾回收和JVM性能优化时。在这个新的Java世界中,我希望和你分享我的这些启发。希望你能像我写这篇文章一样兴奋的去了解JVM的性能。
这个系列文章,是为所有有兴趣去学习更多JVM底层知识和JVM实际做了什么的Java开发人员所写的。在更高层次,我将讨论垃圾回收和在不影响应用运行的情况下,对空闲内存安全和速度上的无止境追求。你将学到JVM的关键部分:垃圾回收和GC算法,编译优化,和一些常用的优化。我同样会讨论为什么Java标记会很难,并提供建议什么时候应该考虑测试性能。最后,我将讲一些JVM和GC的新的创新,包括Azul’s Zing JVM, IBM JVM, 和Oracle’s Garbage First (G1) 垃圾回收中的重点。
我希望你读完这个系列时对Java可扩展性的限制有更深的了解,这样限制是如何强制我们以最优的方式创建一个Java部署。希望你会有一种豁然开朗的感受,并且能激发了一些好的Java灵感:停止接受那些限制,并去改变它!如果你现在还不是一个开源工作者,这个系列或许会鼓励你往这方面发展。
JVM性能调优:阅读该系列
JVM性能和“一次编译,到处运行”的挑战
我有新的消息告诉那些固执的认为Java平台本质上是缓慢的人。当Java刚刚做为企业级应用的时候,JVM被诟病的Java性能问题已经是十几年前的事了,但这个结论,现在已经过时了。这是真的,如果你现在在不同的开发平台上运行简单静态和确定的任务时,你将很可能发现使用机器优化过的代码,比使用任何虚拟环境执行的要好,在相同的JVM下。但是,Java的性能在过去10年有了非常大的提升。Java产业的市场需求和增长,导致了少量的垃圾回收算法、新的编译创新、和大量的启发式方法和优化,这些使JVM技术得到了进步。我将在以后的章节中介绍这些。
JVM的技术之美,同样是它最大的挑战:没有什么可以被认为是“一次编译,到处运行”的应用。不是优化一个用例,一个应用,一个特定的用户负载,JVM不断的跟踪Java应用现在在做什么,并进行相应的优化。这种动态的运行导致了一系列动态的问题。当设计创新时(至少不是在我们向生产环境要性能时),致力于JVM的开发者不会依赖静态编译和可预测的分配率。
JVM性能的事业
在我早期的工作中我意识到垃圾回收是非常难“解决”的,我一直着迷于JVMs和中间件技术。我对JVMs的热情开始于我在JRockit团队中时,编码一种新的方法用于自学,自己调试垃圾回收算法(参考 Resources)。这个项目(转变为JRockit一个实验性的特点,并成为Deterministic Garbage Collection算法的基础)开启了我JVM技术的旅程。我已经在BEA系统、Intel、Sun和Oracle(因为Oracle收购BEA系统,所以被Oracle短暂的工作过)工作过。之后我加入了在Azul Systems的团队去管理Zing JVM,现在我为Cloudera工作。
机器优化的代码可能会实现较好的性能(但这是以牺牲灵活性来做代价的),但对于动态装载和功能快速变化的企业应用,这并不是一个权衡选择它的理由。大多数的企业为了Java的优点,更愿意去牺牲机器优化代码,带来完美的性能。
易于编码和功能开发(意义是更短的时间去响应市场)
得到知识渊博的的程序员
用Java APIs和标准库更快速的开发
可移植性——不用为新的平台去重新写Java应用
从Java代码到字节码
做为一个Java程序员,你可能对编码、编译和执行Java应用很熟悉。例子:我们假设你有一个程序(MyApp.java),现在你想让它运行。去执行这个程序你需要先用javac(JDK内置的静态Java语言到字节码编译器)编译。基于Java代码,javac生成相应的可执行字节码,并保存在相同名字的class文件:MyApp.class中。在把Java代码编译成字节码后,你可以通过java命令(通过命令行或startup脚本,使用不使用startup选项都可以)来启动可执行的class文件,从而运行你的应用。这样你的class被加载到运行时(意味着Java虚拟机的运行),程序开始执行。
这就是表面上每一个应用执行的场景,但是现在我们来探究下当你执行java命令时究竟发生了什么。Java虚拟机是什么?大多数开发人员通过持续调试来与JVM交互——aka selecting 和value-assigning启动选项能让你的Java程序跑的更快,同时避免了臭名昭著的”out of memory”错误。但是,你是否曾经想过,为什么我们起初需要一个JVM来运行Java应用呢?
什么是Java虚拟机?
简单的说,一个JVM是一个软件模块,用于执行Java应用的字节码,并且把字节码转化到硬件,操作系统的指令。通过这样做,JVM允许Java程序在第一次编写后,不需要更改原始的代码,就能在不同的环境中执行。Java的可移植性是通往企业应用语言的关键:开发者并不需要为不同平台重写应用代码,因为JVM负责翻译和平台优化。
一个JVM基本上是一个虚拟的执行环境,作为一个字节码指令机器,而用于分配执行任务和执行内存操作通过与底层的交互。
一个JVM同样为运行的Java应用管理动态资源。这就意味着它掌握分配和释放内存,在每个平台上保持一致的线程模型,在应用执行的地方用一种适于CPU架构的方式组织可执行的指令。JVM把开发人员从需要跟踪对象的引用和存活时长中解放出来。同样的它不用我们管理何时去释放内存——一个像C语言那样的非动态语言的痛点。
你可以把JVM当做是一个专门为Java运行的操作系统;它的工作是为Java应用管理运行环境。一个JVM是一个通过与底层交互的虚拟执行环境,作为一个字节码指令机器,而用于分配执行任务和执行内存操作。
JVM组件概述
有很多写JVM内部和性能优化的文章。作为这个系列的基础,我将会总结概述下JVM组件。这个简短的阅览会为刚接触JVM的开发者有特殊的帮助,会让你了解之后更想深入的讨论。
从一种语言到另一种——关于Java编译器
编译器是输入一种语言,然后输出另一种可执行的语句。Java编译器有两个主要任务:
1. 让Java语言更加轻便,不用每次在特定的平台上写代码。
2. 确保在特定的平台产生有效的可执行的代码。
编译器可以是静态也可以是动态。一个静态编译的例子是javac。它把Java代码当做输入,并转化为字节码(一种在Java虚拟机执行的语言)。静态编译器一次解释输入的代码,输出可执行的形式,这个是在程序执行时将被用到。因为输入是静态的,你将总能看到结果相同。只有当你修改原始代码并重新编译时,你才能看到不同的输出。
动态编译器,例如Just-In-Time (JIT)编译器,把一种语言动态的转化为另一种,这意味着它们在运行时执行代码。一个JIT编译器让你收集或创建运行数据分析(通过插入性能计数的方式实现),并且编译器使用这些环境数据快速的做出决定。动态的编译器在编译的过程中,实现更好的指令序列,把一系列的指令替换成更有效的,并消除多余的操作。随着时间的增长,你将收集更多的代码生成数据,做更多更好的编译决定;整个过程就是我们通常称为的代码优化和重编译。
动态编译给了你根据行为进行动态调整的优势,或随着应用装载次数的增加从而进行新的优化。这就是为什么动态编译器非常适合Java运行。值得注意的是,动态编译器请求外部数据结构,线程资源,CPU周期分析和优化。越深层次的优化,你将需要越多的资源。然而在大多数环境中,顶层对执行性能的提升帮助非常小——比你纯粹的解释要快5到10倍的性能。
分配会导致垃圾回收
每一个线程基于每个“Java进程分配内存地址空间” 完成内存分配,叫Java堆,或简称堆。在Java世界中单线程分配在客户端应用程序中很常见。然而,单线程分配在企业应用和工作装载服务端变的没有任何益处,因为它并没有使用现在多核环境的并行优势。
并行应用设计同样迫使JVM保证在同一时间,多线程不会分配同一个地址空间。你可以通过在整个分配空间中放把锁来控制。但这种技术(通常叫做堆锁)很消耗性能,持有或排队线程会影响资源利用和应用优化的性能。多核系统好的一面是,它们创造了一个需求,为各种各样的新的方法在资源分配的同时去阻止单线程的瓶颈和序列化。
一个常用的方法是把堆分成几部分,在对应用来说每个合适分区大小的地方——显然它们需要调优,分配率和对象大小对不同应用来说有显著的变化,同样线程的数量也不同。线程本地分配缓存(Thread Local Allocation Buffer,简写:TLAB),或者有时,线程本地空间(Thread Local Area,简写:TLA),是一个专门的分区,在其中线程不用声明一个全堆锁就可以自由分配。当区域满的时候,堆就满了,表示堆上的空闲空间不够用来放对象,需要分配空间。当堆满的时候,垃圾回收就会开始。
碎片
使用TLAB捕获异常,是把堆碎片化来降低内存效率。如果一个应用在要分配对象时,正巧不能增加或者不能完全分配一个TLAB空间,这将会有空间太小而不能生成新对象的风险。这样的空闲空间被当做“碎片”。如果应用程序一直保持对象的引用,然后再用剩下的空间分配,最后这些空间会在很长一段时间内空闲。
碎片就是当碎片被分散在堆中的时候——通过一小段不用的内存空间来浪费堆空间。为你的应用分配 “错误的”TLAB空间(关于对象的大小、混合对象的大小和引用持有率)是导致堆内碎片增多的原因。在随着应用的运行,碎片的数量会增加在堆中占有的空间。碎片导致性能下降,系统不能给新应用分配足够的线程和对象。垃圾回收器在随后会很难阻止out-of-memory异常。
TLAB浪费在工作中产生。一种方法可以完全或暂时避免碎片,那就是在每次基础操作时优化TLAB空间。这种方法典型的作法是应用只要有分配行为,就需要重新调优。通过复杂的JVM算法可以实现,另一种方法是组织堆分区实现更有效的内存分配。例如,JVM可以实现free-lists,它是连接起一串特定大小的空闲内存块。一个连续的空闲内存块和另一个相同大小的连续内存块相连,这样会创建少量的链表,每个都有自己的边界。在有些情况下free-lists导致更好的合适内存分配。线程可以对象分配在一个差不多大小的块中,这样比你只依靠固定大小的TLAB,潜在的产生少的碎片。
GC琐事
有一些早期的垃圾收集器拥有多个老年代,但是当超过两个老年代的时候会导致开销超过价值。另一种优化分配减少碎片的方法,就是创造所谓的新生代,这是一个专门用于分配新对象的专用堆空间。剩余的堆会成为所谓的老年代。老年代是用来分配长时间存在的对象的,被假定会存在很长时间的对象包括不被垃圾收集的对象或者大对象。为了更好的理解这种分配的方法,我们需要讲一些垃圾收集的知识。
垃圾回收和应用性能
垃圾回收是JVM的垃圾回收器去释放没有引用的被占据的堆内存。当第一次触发垃圾收集时,所有的对象引用还被保存着,被以前的引用占据的空间被释放或重新分配。当所有可回收的内存被收集后,空间等待被抓取和再次分配给新对象。
垃圾回收器永远都不能重声明一个引用对象,这样做会破坏JVM的标准规范。这个规则的异常是一个可以捕获的soft或weak引用 ,如果垃圾收集器将要将近耗尽内存。我强烈推荐你尽量避免weak引用,然而,因为Java规范的模糊导致了错误的解释和使用的错误。更何况,Java是被设计为动态内存管理,因为你不需要考虑什么时候和什么地方释放内存。
垃圾收集器的一个挑战是在分配内存时,需要尽量不影响运行着的应用。如果你不尽量垃圾收集,你的应用将耗近内存;如果你收集的太频繁,你将损失吞吐量和响应时间,这将对运行的应用产生坏的影响。
GC算法
有许多不同的垃圾回收算法。稍后,在这个系列里将深入讨论几种。在最高层,垃圾收集最主要的两个方法是引用计数和跟踪收集器。
引用计数收集器会跟踪一个对象指向多少个引用。当一个对象的引用为0时,内存将被立即回收,这是这种方法的优点之一。引用计数方法的难点在于环形数据结构和保持所有的引用即时更新。
跟踪收集器对仍在引用的对象标记,用已经标记的对象,反复的跟随和标记所有的引用对象。当所有的仍然引用的对象被标记为“live”时,所有的不被标记的空间将被回收。这种方法用于管理环形数据结构,但是在很多情况下收集器在回收不被引用的内存之前,需要等待所有标记完成。
有不种的算法来实现上面的方法。最著名的算法是 marking 或copying 算法, parallel 或 concurrent算法。我将在稍后的文章中讨论这些。
通常来说垃圾回收的意义是,致力于在堆中给新对象和老对象分配地址空间。其中“老对象”是指在许多垃圾回收后幸存的对象。用新生代来存放新对象,老年代存放老对象,这样能通过快速回收短时间占据内存的对象来减少碎片,同样通过把长时间存在的对象聚合在一起,并把它们放到老年代地址空间中。所有这些在长时间对象和保存堆内存不碎片化之间减少了碎片。新生代的一个积极作用是延迟了需要花费更大代价回收老年代对象的时间,你可以为短暂的对象重复利用相同的空间。(老空间的收集会花费更多,是因为长时间存在的对象们,会包含更多的引用,需要更多的遍历。)
最后值的一提的算法是compaction,这是管理内存碎片的方法。Compaction基本来说就是把对象移动到一起,从来释放更大的连续内存空间。如果你熟悉磁盘碎片和处理它的工具,你会发现compaction跟它很像,不同的是这个运行在Java堆内存中。我将在系列中详细讨论compaction。
总结:回顾和重点
JVM允许可移植(一次编程,到处运行)和动态的内存管理,所有Java平台的主要特性,都是它受欢迎和提高生产力的原因。
在第一篇JVM性能优化系统的文章中我解释了一个编译器怎么把字节码转化为目标平台的指令语言的,并帮助动态的优化Java程序的执行。不同的应用需要不同的编译器。
我同样简述了内存分配和垃圾收集,和这些怎么与Java应用性能相关的。基本上,你越快的填满堆和频繁的触发垃圾收集,Java应用的占有率越高。垃圾收集器的一个挑战是在分配内存时,在应用耗尽内存之前回收内存,但是尽量不影响运行着的应用。在以后的文章中我们会更详细的讨论传统的和新的垃圾回收和JVM性能优化。
注:该博文转自并发编程网 http://ifeve.com/jvm-optimize-1/
如果你是一个开发人员,你肯定遇到过这样的特殊感觉,你突然灵光一现,所有的思路连接起来了,你能以一个新的视角来回想起你以前的想法。我个人很喜欢学习新知识带来的这种感觉。我已经有过很多次这样的经历了,在我使用JVM技术工作时,特别是使用垃圾回收和JVM性能优化时。在这个新的Java世界中,我希望和你分享我的这些启发。希望你能像我写这篇文章一样兴奋的去了解JVM的性能。
这个系列文章,是为所有有兴趣去学习更多JVM底层知识和JVM实际做了什么的Java开发人员所写的。在更高层次,我将讨论垃圾回收和在不影响应用运行的情况下,对空闲内存安全和速度上的无止境追求。你将学到JVM的关键部分:垃圾回收和GC算法,编译优化,和一些常用的优化。我同样会讨论为什么Java标记会很难,并提供建议什么时候应该考虑测试性能。最后,我将讲一些JVM和GC的新的创新,包括Azul’s Zing JVM, IBM JVM, 和Oracle’s Garbage First (G1) 垃圾回收中的重点。
我希望你读完这个系列时对Java可扩展性的限制有更深的了解,这样限制是如何强制我们以最优的方式创建一个Java部署。希望你会有一种豁然开朗的感受,并且能激发了一些好的Java灵感:停止接受那些限制,并去改变它!如果你现在还不是一个开源工作者,这个系列或许会鼓励你往这方面发展。
JVM性能调优:阅读该系列
- 第一部分:概述
- 第二部分:编译工具
- 第三部分:垃圾回收
- 第四部分:并发压缩GC
- 第五部分:可扩展性
JVM性能和“一次编译,到处运行”的挑战
我有新的消息告诉那些固执的认为Java平台本质上是缓慢的人。当Java刚刚做为企业级应用的时候,JVM被诟病的Java性能问题已经是十几年前的事了,但这个结论,现在已经过时了。这是真的,如果你现在在不同的开发平台上运行简单静态和确定的任务时,你将很可能发现使用机器优化过的代码,比使用任何虚拟环境执行的要好,在相同的JVM下。但是,Java的性能在过去10年有了非常大的提升。Java产业的市场需求和增长,导致了少量的垃圾回收算法、新的编译创新、和大量的启发式方法和优化,这些使JVM技术得到了进步。我将在以后的章节中介绍这些。
JVM的技术之美,同样是它最大的挑战:没有什么可以被认为是“一次编译,到处运行”的应用。不是优化一个用例,一个应用,一个特定的用户负载,JVM不断的跟踪Java应用现在在做什么,并进行相应的优化。这种动态的运行导致了一系列动态的问题。当设计创新时(至少不是在我们向生产环境要性能时),致力于JVM的开发者不会依赖静态编译和可预测的分配率。
JVM性能的事业
在我早期的工作中我意识到垃圾回收是非常难“解决”的,我一直着迷于JVMs和中间件技术。我对JVMs的热情开始于我在JRockit团队中时,编码一种新的方法用于自学,自己调试垃圾回收算法(参考 Resources)。这个项目(转变为JRockit一个实验性的特点,并成为Deterministic Garbage Collection算法的基础)开启了我JVM技术的旅程。我已经在BEA系统、Intel、Sun和Oracle(因为Oracle收购BEA系统,所以被Oracle短暂的工作过)工作过。之后我加入了在Azul Systems的团队去管理Zing JVM,现在我为Cloudera工作。
机器优化的代码可能会实现较好的性能(但这是以牺牲灵活性来做代价的),但对于动态装载和功能快速变化的企业应用,这并不是一个权衡选择它的理由。大多数的企业为了Java的优点,更愿意去牺牲机器优化代码,带来完美的性能。
易于编码和功能开发(意义是更短的时间去响应市场)
得到知识渊博的的程序员
用Java APIs和标准库更快速的开发
可移植性——不用为新的平台去重新写Java应用
从Java代码到字节码
做为一个Java程序员,你可能对编码、编译和执行Java应用很熟悉。例子:我们假设你有一个程序(MyApp.java),现在你想让它运行。去执行这个程序你需要先用javac(JDK内置的静态Java语言到字节码编译器)编译。基于Java代码,javac生成相应的可执行字节码,并保存在相同名字的class文件:MyApp.class中。在把Java代码编译成字节码后,你可以通过java命令(通过命令行或startup脚本,使用不使用startup选项都可以)来启动可执行的class文件,从而运行你的应用。这样你的class被加载到运行时(意味着Java虚拟机的运行),程序开始执行。
这就是表面上每一个应用执行的场景,但是现在我们来探究下当你执行java命令时究竟发生了什么。Java虚拟机是什么?大多数开发人员通过持续调试来与JVM交互——aka selecting 和value-assigning启动选项能让你的Java程序跑的更快,同时避免了臭名昭著的”out of memory”错误。但是,你是否曾经想过,为什么我们起初需要一个JVM来运行Java应用呢?
什么是Java虚拟机?
简单的说,一个JVM是一个软件模块,用于执行Java应用的字节码,并且把字节码转化到硬件,操作系统的指令。通过这样做,JVM允许Java程序在第一次编写后,不需要更改原始的代码,就能在不同的环境中执行。Java的可移植性是通往企业应用语言的关键:开发者并不需要为不同平台重写应用代码,因为JVM负责翻译和平台优化。
一个JVM基本上是一个虚拟的执行环境,作为一个字节码指令机器,而用于分配执行任务和执行内存操作通过与底层的交互。
一个JVM同样为运行的Java应用管理动态资源。这就意味着它掌握分配和释放内存,在每个平台上保持一致的线程模型,在应用执行的地方用一种适于CPU架构的方式组织可执行的指令。JVM把开发人员从需要跟踪对象的引用和存活时长中解放出来。同样的它不用我们管理何时去释放内存——一个像C语言那样的非动态语言的痛点。
你可以把JVM当做是一个专门为Java运行的操作系统;它的工作是为Java应用管理运行环境。一个JVM是一个通过与底层交互的虚拟执行环境,作为一个字节码指令机器,而用于分配执行任务和执行内存操作。
JVM组件概述
有很多写JVM内部和性能优化的文章。作为这个系列的基础,我将会总结概述下JVM组件。这个简短的阅览会为刚接触JVM的开发者有特殊的帮助,会让你了解之后更想深入的讨论。
从一种语言到另一种——关于Java编译器
编译器是输入一种语言,然后输出另一种可执行的语句。Java编译器有两个主要任务:
1. 让Java语言更加轻便,不用每次在特定的平台上写代码。
2. 确保在特定的平台产生有效的可执行的代码。
编译器可以是静态也可以是动态。一个静态编译的例子是javac。它把Java代码当做输入,并转化为字节码(一种在Java虚拟机执行的语言)。静态编译器一次解释输入的代码,输出可执行的形式,这个是在程序执行时将被用到。因为输入是静态的,你将总能看到结果相同。只有当你修改原始代码并重新编译时,你才能看到不同的输出。
动态编译器,例如Just-In-Time (JIT)编译器,把一种语言动态的转化为另一种,这意味着它们在运行时执行代码。一个JIT编译器让你收集或创建运行数据分析(通过插入性能计数的方式实现),并且编译器使用这些环境数据快速的做出决定。动态的编译器在编译的过程中,实现更好的指令序列,把一系列的指令替换成更有效的,并消除多余的操作。随着时间的增长,你将收集更多的代码生成数据,做更多更好的编译决定;整个过程就是我们通常称为的代码优化和重编译。
动态编译给了你根据行为进行动态调整的优势,或随着应用装载次数的增加从而进行新的优化。这就是为什么动态编译器非常适合Java运行。值得注意的是,动态编译器请求外部数据结构,线程资源,CPU周期分析和优化。越深层次的优化,你将需要越多的资源。然而在大多数环境中,顶层对执行性能的提升帮助非常小——比你纯粹的解释要快5到10倍的性能。
分配会导致垃圾回收
每一个线程基于每个“Java进程分配内存地址空间” 完成内存分配,叫Java堆,或简称堆。在Java世界中单线程分配在客户端应用程序中很常见。然而,单线程分配在企业应用和工作装载服务端变的没有任何益处,因为它并没有使用现在多核环境的并行优势。
并行应用设计同样迫使JVM保证在同一时间,多线程不会分配同一个地址空间。你可以通过在整个分配空间中放把锁来控制。但这种技术(通常叫做堆锁)很消耗性能,持有或排队线程会影响资源利用和应用优化的性能。多核系统好的一面是,它们创造了一个需求,为各种各样的新的方法在资源分配的同时去阻止单线程的瓶颈和序列化。
一个常用的方法是把堆分成几部分,在对应用来说每个合适分区大小的地方——显然它们需要调优,分配率和对象大小对不同应用来说有显著的变化,同样线程的数量也不同。线程本地分配缓存(Thread Local Allocation Buffer,简写:TLAB),或者有时,线程本地空间(Thread Local Area,简写:TLA),是一个专门的分区,在其中线程不用声明一个全堆锁就可以自由分配。当区域满的时候,堆就满了,表示堆上的空闲空间不够用来放对象,需要分配空间。当堆满的时候,垃圾回收就会开始。
碎片
使用TLAB捕获异常,是把堆碎片化来降低内存效率。如果一个应用在要分配对象时,正巧不能增加或者不能完全分配一个TLAB空间,这将会有空间太小而不能生成新对象的风险。这样的空闲空间被当做“碎片”。如果应用程序一直保持对象的引用,然后再用剩下的空间分配,最后这些空间会在很长一段时间内空闲。
碎片就是当碎片被分散在堆中的时候——通过一小段不用的内存空间来浪费堆空间。为你的应用分配 “错误的”TLAB空间(关于对象的大小、混合对象的大小和引用持有率)是导致堆内碎片增多的原因。在随着应用的运行,碎片的数量会增加在堆中占有的空间。碎片导致性能下降,系统不能给新应用分配足够的线程和对象。垃圾回收器在随后会很难阻止out-of-memory异常。
TLAB浪费在工作中产生。一种方法可以完全或暂时避免碎片,那就是在每次基础操作时优化TLAB空间。这种方法典型的作法是应用只要有分配行为,就需要重新调优。通过复杂的JVM算法可以实现,另一种方法是组织堆分区实现更有效的内存分配。例如,JVM可以实现free-lists,它是连接起一串特定大小的空闲内存块。一个连续的空闲内存块和另一个相同大小的连续内存块相连,这样会创建少量的链表,每个都有自己的边界。在有些情况下free-lists导致更好的合适内存分配。线程可以对象分配在一个差不多大小的块中,这样比你只依靠固定大小的TLAB,潜在的产生少的碎片。
GC琐事
有一些早期的垃圾收集器拥有多个老年代,但是当超过两个老年代的时候会导致开销超过价值。另一种优化分配减少碎片的方法,就是创造所谓的新生代,这是一个专门用于分配新对象的专用堆空间。剩余的堆会成为所谓的老年代。老年代是用来分配长时间存在的对象的,被假定会存在很长时间的对象包括不被垃圾收集的对象或者大对象。为了更好的理解这种分配的方法,我们需要讲一些垃圾收集的知识。
垃圾回收和应用性能
垃圾回收是JVM的垃圾回收器去释放没有引用的被占据的堆内存。当第一次触发垃圾收集时,所有的对象引用还被保存着,被以前的引用占据的空间被释放或重新分配。当所有可回收的内存被收集后,空间等待被抓取和再次分配给新对象。
垃圾回收器永远都不能重声明一个引用对象,这样做会破坏JVM的标准规范。这个规则的异常是一个可以捕获的soft或weak引用 ,如果垃圾收集器将要将近耗尽内存。我强烈推荐你尽量避免weak引用,然而,因为Java规范的模糊导致了错误的解释和使用的错误。更何况,Java是被设计为动态内存管理,因为你不需要考虑什么时候和什么地方释放内存。
垃圾收集器的一个挑战是在分配内存时,需要尽量不影响运行着的应用。如果你不尽量垃圾收集,你的应用将耗近内存;如果你收集的太频繁,你将损失吞吐量和响应时间,这将对运行的应用产生坏的影响。
GC算法
有许多不同的垃圾回收算法。稍后,在这个系列里将深入讨论几种。在最高层,垃圾收集最主要的两个方法是引用计数和跟踪收集器。
引用计数收集器会跟踪一个对象指向多少个引用。当一个对象的引用为0时,内存将被立即回收,这是这种方法的优点之一。引用计数方法的难点在于环形数据结构和保持所有的引用即时更新。
跟踪收集器对仍在引用的对象标记,用已经标记的对象,反复的跟随和标记所有的引用对象。当所有的仍然引用的对象被标记为“live”时,所有的不被标记的空间将被回收。这种方法用于管理环形数据结构,但是在很多情况下收集器在回收不被引用的内存之前,需要等待所有标记完成。
有不种的算法来实现上面的方法。最著名的算法是 marking 或copying 算法, parallel 或 concurrent算法。我将在稍后的文章中讨论这些。
通常来说垃圾回收的意义是,致力于在堆中给新对象和老对象分配地址空间。其中“老对象”是指在许多垃圾回收后幸存的对象。用新生代来存放新对象,老年代存放老对象,这样能通过快速回收短时间占据内存的对象来减少碎片,同样通过把长时间存在的对象聚合在一起,并把它们放到老年代地址空间中。所有这些在长时间对象和保存堆内存不碎片化之间减少了碎片。新生代的一个积极作用是延迟了需要花费更大代价回收老年代对象的时间,你可以为短暂的对象重复利用相同的空间。(老空间的收集会花费更多,是因为长时间存在的对象们,会包含更多的引用,需要更多的遍历。)
最后值的一提的算法是compaction,这是管理内存碎片的方法。Compaction基本来说就是把对象移动到一起,从来释放更大的连续内存空间。如果你熟悉磁盘碎片和处理它的工具,你会发现compaction跟它很像,不同的是这个运行在Java堆内存中。我将在系列中详细讨论compaction。
总结:回顾和重点
JVM允许可移植(一次编程,到处运行)和动态的内存管理,所有Java平台的主要特性,都是它受欢迎和提高生产力的原因。
在第一篇JVM性能优化系统的文章中我解释了一个编译器怎么把字节码转化为目标平台的指令语言的,并帮助动态的优化Java程序的执行。不同的应用需要不同的编译器。
我同样简述了内存分配和垃圾收集,和这些怎么与Java应用性能相关的。基本上,你越快的填满堆和频繁的触发垃圾收集,Java应用的占有率越高。垃圾收集器的一个挑战是在分配内存时,在应用耗尽内存之前回收内存,但是尽量不影响运行着的应用。在以后的文章中我们会更详细的讨论传统的和新的垃圾回收和JVM性能优化。
注:该博文转自并发编程网 http://ifeve.com/jvm-optimize-1/
发表评论
-
for循环删除(ArrayList.remove)报错及解决办法
2017-01-12 15:50 1795[size=small]代码: JSONArray bin ... -
DES/3DES/AES区别
2016-12-19 14:29 728公元前400年,古希腊人 ... -
运用@Transactional,自己抛出异常时不会回滚的原因
2016-12-12 16:56 717一、 当你读这篇文章的 ... -
Linux 与 BSD 有什么不同?(转载)
2016-12-06 20:19 632Linux 和 BSD 都是免费的,开源的,类Unix系统。 ... -
OJDBC版本区别 [ojdbc14.jar,ojdbc5.jar和ojdbc6.jar的区别]
2016-11-21 14:22 1667在使用Oracle JDBC驱动时 ... -
查看jvm栈深度
2016-10-28 18:44 895jinfo -flag ThreadStackSize 113 ... -
Java加密技术-常用加解密算法、签名算法(转载)
2016-10-23 14:46 654http://snowolf.iteye.com/blog/3 ... -
《 Java并发编程从入门到精通》 常见的内存溢出的三种情况(转载)
2016-10-19 20:08 646鸟欲高飞先振翅,人求 ... -
Java内存模型
2016-10-07 10:20 526一、内存模型 ... -
JVM垃圾收集器种类及特点
2016-10-04 09:41 2414垃圾收集器是内存 ... -
再谈引用(强引用,软引用,弱引用,虚引用)的概念,回收方法区
2016-10-03 09:41 1124再谈引用 无论是通过引用计数算法判断对象的引 ... -
JVM垃圾收集算法
2016-10-03 15:04 604在JVM中,程序计数 ... -
JVM运行时数据区域名词解释
2016-10-01 22:54 658运行时数据区域,Java虚 ... -
类加载器的双亲委派模型
2016-10-01 10:55 954双亲委派模型 从Java虚拟机的角度来讲,只存在两种 ... -
Java中使用HttpRequest获取用户真实IP地址(转载)
2016-09-18 15:50 1348[size=medium] 在JSP里,获取客户端的IP ... -
Truncate Table 用法(转载)
2016-09-18 10:42 978TRUNCATE TABLE 删除表中的所有行,而不记录单个行 ... -
Java虚拟机类加载器机制-Java类加载器的时机、过程
2016-09-17 23:44 607一、定义:虚拟机把描 ... -
稳定排序和不稳定排序(转载)
2016-08-14 17:56 511本文是针对老是记不住 ... -
StackOverflow:你没见过的七个最好的Java答案(转载)
2016-08-12 00:10 581StackOverflow(后边简称so) ... -
使用 WebSockets 技术的 9 个应用场景
2016-08-08 17:35 639没有其他技术能够像WebSocket一样提供真正的双向通信 ...
相关推荐
JVM性能优化是一项细致而关键的任务,能够显著提升程序的运行效率,减少资源消耗,提高系统稳定性。本PPT详细探讨了JVM的工作原理以及如何进行性能优化。 1. **JVM架构** JVM分为几个主要区域:方法区、堆、栈、...
Jvm性能优化是Java虚拟机(JVM)中非常重要的一部分,它对Jvm的性能产生了很大的影响。本文将从Jvm内存结构的角度来分析Jvm性能优化的原理。 Jvm内存结构主要分为五部分:堆、虚拟机栈、本地方法栈、程序计数器和...
JVM性能优化笔记 ------------------------------------------
### jvm性能优化相关知识点详解 #### 一、JVM加载Class文件的原理机制 **1.1 类加载概述** Java虚拟机(JVM)在执行Java程序时,并不会一次性加载所有的类,而是采取按需加载的方式。这种方式可以有效减少内存消耗...
本次分享的内容将涵盖Java类的加载过程、类加载机制、内存分配和垃圾收集机制等关键知识点,这些内容对于理解Java程序的性能优化至关重要。 首先,我们来探讨Java类加载的过程,这个过程包括了加载、验证、准备、...
深入理解JVM对于优化Java应用程序的性能至关重要。本篇文章将详细探讨JVM的工作原理,并提供一些有效的性能优化策略。 首先,JVM的内存结构分为堆、栈、方法区、程序计数器、本地方法栈等几个关键区域。堆是所有...
标题《JVM系列之性能调优参考手册(实践篇)》涉及的知识点主要集中在Java虚拟机(JVM)性能调优的实践操作。JVM作为Java程序运行的基础环境,对程序性能有着决定性影响。本手册的目的是指导开发者如何对JVM进行性能...
JVM性能监控与调优实战课程,作为整篇课程的重中之重,非常值得同学们参考学习。课程前端讲解了JVM的性能监控和调优的概述,对调优的的方法和工具进行讲解学习,让同学们掌握方法,理解知识。课程的中间阶段我们进行...
《实战JAVA虚拟机—JVM故障诊断与性能优化》是一本深入探讨Java虚拟机(JVM)技术的书籍,旨在帮助开发者和系统管理员诊断并优化JVM相关的性能问题。本书内容丰富,涵盖了大量的实践案例,使得即便是初学者也能理解...
在实际开发中,对JVM进行性能优化是一项至关重要的任务。以下是一些关于JVM性能优化的关键知识点: 1. **内存管理:** - **堆内存**:Java对象主要存储在堆内存中,分为新生代(Young Generation)和老年代(Old ...
总之,Java虚拟机的性能优化是一个涉及多方面知识的复杂过程,需要深入理解JVM的内部机制,结合实际应用特点,合理调整JVM参数,才能实现高效、稳定的运行。通过对JVM的深入学习和实践,开发者能够提升Java应用程序...
《实战Java虚拟机——JVM故障诊断与性能优化》内容简介:随着越来越多的第三方语言(Groovy、Scala、JRuby等)在Java虚拟机上运行,Java也俨然成为一个充满活力的生态圈。本书将通过200余示例详细介绍Java虚拟机中的...
在深入探讨JVM性能优化相关面试题之前,先了解Java虚拟机(JVM)中类加载过程、JVM加载Class文件的原理机制以及Java内存分配的知识点是非常有必要的。 首先,Java类加载过程共有七个步骤,这七个步骤分别是:加载、...
实战JAVA虚拟机 JVM故障诊断与性能优化.pdf (无书签.低分放送) 实战JAVA虚拟机 JVM故障诊断与性能优化.pdf (无书签.低分放送) 实战JAVA虚拟机 JVM故障诊断与性能优化.pdf (无书签.低分放送)
JVM性能优化相关问题.zip
在Java开发领域,JVM(Java Virtual Machine)和性能优化是至关重要的主题。这份"JVM和性能优化学习思维笔记"旨在帮助开发者深入理解和实践Java应用程序的性能提升。以下是基于标题、描述和标签所涉及的知识点的详细...
1、Java内存区域 2、垃圾回收器和内存分配策略 3、JVM的执行子系统 4、编写高效优雅Java程序 5、深入了解性能优化
JVM性能优化是提升Java应用效率的关键环节,涉及到内存管理、垃圾回收、线程调度等多个方面。以下是对"Java JVM及性能优化"和"Java性能分析"的详细阐述: 一、JVM结构与工作原理 1. 类装载器:负责加载类文件,确保...
性能优化手册是一套java性能学习研究小技巧,包含内容:Java性能优化、JVM性能优化、服务器性能优化、数据库性能优化、前端性能优化等。 内容包括但不限于: String 性能优化的 3 个小技巧 HashMap 7 种遍历方式...