内存管理的方式
内存管理有显式内存管理和隐式内存管理(交给垃圾收集器处理)两种方式。
显式内存管理会带来两个问题,一是悬空引用(一个对象仍然被其他对象引用着,但它的空间却被回收、重新分配了),另一个是空间泄漏(对象已经不再被引用了,但分配给它的内存没被释放)。
如果交给垃圾收集器来进行内存管理,垃圾收集器就需要负责:
- 分配内存
- 保证被引用的对象都存在在内存中
- 对象不可达时,回收它占用的内存
Java采用的是隐式内存管理,JVM会提供垃圾收集器完成内存的分配和回收。
垃圾收集器
垃圾收集器在工作过程中,要么和应用程序并行执行,收集垃圾的时候不影响应用程序的运行,要么就和应用程序顺序执行,中断应用程序(stop-the-world)进行垃圾收集。对于收集之后产生的内存碎片,有压缩(将live对象移到一起)、不压缩(速度快,但产生碎片)和拷贝(将live对象拷贝到不同的内存区域)三种方式。
而对垃圾收集器整体来说,除了前面所说的需求和工作方式外,还要考虑一些性能指标:
- 吞吐量——非垃圾回收时间所占的百分比
- 垃圾回收开销——垃圾回收时间所占的百分比
- 暂停时间——停止应用、执行垃圾收集的时间
- 收集频率
- footprint——度量收集回来的内存大小
- 及时性——对象成为垃圾之后,到占用的内存变得可用所需的时间
HotSpot JVM里的垃圾收集器( Java SE 7.0 之前)
HotSpot JVM将内存划分为年轻代、老年代和永久代。年轻代——大部分对象初始化时都分配到此区域,里面包含一个eden区(对象初始分配在eden里)和两个survivor区(一个保持空,另一个存放存活下来的对象)。老年代——一些大对象初始化时会分配到此区域;年轻代收集若干次之后存活的对象也会提升到老年代中。永久代——存放描述类和方法的对象;还有类和方法本身。
下面看看HotSpot JVM里的垃圾收集器。
顺序收集器
顾名思义是顺序执行的,所以会stop-the-world,适用于对暂停时间要求不高的应用。在Java里,顺序收集器是非服务器级别机器上缺省的垃圾收集器。可以使用-XX:+UseSerialGC指定。
顺序收集器在年轻代的行为
eden区域的live对象拷贝到空的survivor空间(To),相对survivor来说较大的live对象会直接拷贝到老年代。
另一个survivor空间(From)里的live年轻对象也会拷贝到To survivor空间,相对老的live对象会拷贝到老年代。
如果To survivor空间满了,eden和From survivor里的live对象就会拷贝到老年代去。
最终,eden区域和From区域会被清空。两个survivor区域互换角色。
顺序收集器在老年代的行为
老年代和永久代都使用mark-sweep-compact算法。
标记阶段,收集器辨别仍然活着的对象。
清理阶段,清理代中的“垃圾”。
移动压缩阶段,将live对象移动到老年代空间的开始处(永久代也一样),留出后面连续的空闲空间。之后的分配使用bump-the-pointer技术,会比较快速。
并行收集器(吞吐量收集器)
并行收集器是服务器级别机器上缺省的垃圾收集器。可以使用-XX:+UseParallelGC指定。
并行收集器在年轻代的行为
算法是顺序收集器在年轻代的算法的并行版本,仍然是stop-the-world和拷贝。
并行收集器在老年代的行为
算法仍然是mark-sweep-compact。
从并行收集器的算法可以看出,它适用于在多核机器上运行、但对暂停时间的要求也不高的应用。常见的有批处理、科学计算、计费类应用。
并行压缩收集器
并行压缩收集器从Java 5.0的update 6开始引入。可以使用-XX:+UseParallelOldGC指定。
和并行收集器的区别在于老年代垃圾回收的算法。老年代和永久代都用stop-the-world和移动压缩,不同在于是并行的。
- 首先每个代逻辑划分成固定大小的区域。在标记阶段,对象会分配给垃圾收集线程,并行标记live对象。一个对象标记为live对象后,会记录对象的大小和位置。
- summary阶段对区域进行操作,而不是对象。因为前一次收集压缩之后,live对象往往集中在代的左端,此时对密度比较大的区域进行压缩会不太值得。所以,summary阶段首先是从最左端检查各个区域的密度,直到找到一个能恢复的、而且它右边的区域值得压缩的点。这个点左边的区域叫做“dense prefix”(密度前缀),不会被移动这些区域里的对象。这个点右边的区域会被压缩。对于每个被压缩过的区域,summary阶段会计算并存储里面live对象第一个字节的新位置。需要注意的是,目前的summary阶段还是顺序的,并行化带来的性能改进不及标记和压缩阶段的并行化。
- 压缩阶段,垃圾收集线程使用summary数据找出需要填充的区域,各个线程并行把数据拷贝到那些区域中。这样处理之后,堆的一头密度就会较大,另一头则会有空的区域。
并行压缩收集器的适用场景和并行收集器类似,但如果对暂停时间的要求高一些的话,并行压缩收集器要比并行收集器好。可以使用–XX:ParallelGCThreads=n指定线程数。
CMS(concurrent mark-sweep)收集器
CMS收集器也叫低延迟收集器,能减少暂停的时间,提升应用的响应时间。可以使用–XX:+UseConcMarkSweepGC指定。
CMS收集器在年轻代的行为
收集和并行收集器在年轻代的行为一样。
CMS收集器在老年代的行为
大部分收集和应用的执行是并行的。
CMS收集器的收集周期始于一个短暂的暂停,叫做“initial mark”,找出应用代码直接可达的live对象初始集合。接着是并行标记阶段,标记通过初始集合能到达的所有live对象。由于应用在运行,收集器标记的同时引用情况会更新,所以并行标记阶段结束的时候,并不能保证所有的live对象都被标记到。为了解决这个问题,应用会再次暂停一下,叫做“remark”,完成live对象的最终标记。remark暂停比initial mark的暂停时间长,所以会用多线程并发执行,来提升效率。
接着进入并行sweep阶段,基于remark阶段的标记,清除掉所有垃圾。下图是顺序mark-sweep-compact收集器和CMS收集器在老年代收集上的区别。
利弊分析
CMS收集器是唯一不进行压缩的收集器。释放垃圾占用的空间后,不会把live对象移到老年代的一头。这会节省GC的时间,但会有碎片,而且由于没有连续的空闲空间,收集器在给进入老年代的对象分配内存时会比较昂贵。老年代主要是给年轻代收集之后晋升上来的对象分配空间,所以不压缩也会给年轻代带来一些额外的开销。
和其他收集器相比,CMS收集器的另一个缺陷是需要较大的堆空间。标记阶段应用还在运行,所以会继续分配内存,老年代就会继续增长。另外,标记阶段虽然会识别出所有的live对象,但其中一些对象可能会在标记阶段变成垃圾,下一次老年代收集时才会收集这些对象。
CMS收集器还有一个独特之处,老年代的收集动作并不是在老年代满的时候开始,而是会尽早进行。要是老年代满了才开始收集,CMS收集器会切换成stop-the-world的mark-sweep-compact算法。为了避免这种情况,CMS收集器会根据先前的收集次数和老年代消耗的速度决定收集开始的时间。另外,也可以在命令行设置–XX:CMSInitiatingOccupancyFraction=n选项,其中n表示老年代的初始占用率(百分比),一旦超过n,CMS收集器就会开始收集。n的缺省值是68。
总的来说,CMS收集器能减少老年代暂停,但会略微增加年轻代的暂停时间,损失一点点吞吐量,而且对堆大小的要求会更高一些。
增量模式
CMS收集器的并行阶段可以增量进行,就是阶段性地停止并行阶段,将处理器让给应用。应用如果运行在处理器较少(比如1或2个)的机器上,要求暂停时间比较短,就可以选择增量模式。
如果应用要求垃圾收集暂停时间比较短,又能在运行时和垃圾收集器共享处理器资源,就可以使用CMS收集器。应用里长期存活的数据要是相对比较多,而且应用运行的机器有两个或多个处理器,应用采用CMS收集器就比较好,比如Web服务器。
Ergonomics——自动选择和调优
从Java 5.0起,JVM能根据应用运行的平台和操作系统自动选择垃圾收集器、堆大小、以及HotSpot虚拟机类型(client还是server),能更好地满足不同类型应用的需求,减少命令行选项的设置。此外,并行垃圾收集器也能动态调优。根据平台自动选择和垃圾收集调优综合起来就是Ergonomics。Ergonomics的目标就是减少命令行参数,但提升JVM的性能。
垃圾收集器、堆大小和虚拟机的自动选择
服务器级别的机器指具备两个或多个物理处理器、2G及以上物理内存的非32位Windows平台。
对非服务器级别的机器来说,JVM、垃圾收集器、堆大小的缺省值是:
- client JVM
- 顺序垃圾收集器
- 初始堆大小是4MB,最大堆大小是64MB
对Server级别的机器来说,缺省值是:
- server JVM(除非显式指定-client命令行选项)
- server JVM选择并行垃圾收集器,client JVM选择顺序垃圾收集器
- 垃圾收集器如果是并行垃圾收集器,初始堆大小是物理内存的1/64(上限1GB),最大堆大小是物理内存的1/4(上限是1GB)。其他情况下初始堆大小是4MB,最大堆大小是64MB
并行收集器调优
从Java SE 5.0开始,并行垃圾收集器能自动调优,以满足应用在命令行指定的最大暂停时间和吞吐量。
最大暂停时间
使用命令行选项-XX:MaxGCPauseMillis=n(毫秒)指定并行收集器的最大暂停时间。
并行收集器会根据堆大小和其他垃圾收集相关的参数进行调整,确保暂停时间小于n毫秒。不过这种调整可能会降低应用总的吞吐量,有些情况下也可能满足不了需求。
最大暂停时间对每个代是单独生效的,满足不了需求的时候,会减小代的大小。缺省没有暂停时间的限定。
吞吐量
吞吐量使用命令行选项-XX:GCTimeRatio=n来指定。垃圾收集花费的时间和应用运行的时间之间的比率就是1/(1+n)。n的缺省值是99,也就是垃圾收集花费的时间占总时间的1%。
垃圾收集花费的时间适用于所有代。满足不了需求的时候,会增加代的大小。
Footprint
如果吞吐量和最大暂停时间都满足了,垃圾收集器会减小堆的大小,直到满足不了其中一个目标(必然是吞吐量)。然后垃圾收集器又会进行调整,以达到指定的目标。
目标优先级
并行垃圾收集器首先会满足最大暂停时间,达到目标后才会调整去满足吞吐量目录。最大暂停时间和吞吐量都满足后才会考虑Footprint。
建议
Ergonomics技术会自动选择垃圾收集器、虚拟机和堆大小。所以在做应用调优时,先不要自己显式指定垃圾收集器等设置,先让系统根据应用所在的平台和OS进行自动选择,然后进行测试,垃圾收集相关的性能结果不好再调整垃圾收集器参数。然后持续进行“测试--调优”的过程。
分析性能的工具和常用选项将在后面介绍。
相关推荐
### 性能工程师指南:玩转OpenJDK HotSpot垃圾收集器 #### 一、性能工程概述 在软件开发过程中,性能工程是一个重要的环节,它不仅涵盖了对软件性能的需求定义与测试计划制定,还包括了软件的开发、实施及后续的...
Hotspot JVM 是 Java 虚拟机的一种实现,它提供了以下几个组件: * 字节码执行:使用解释器、两种运行时编译器或 On-Stack Replacement * 存储分配和垃圾回收 * 运行时环境:包括启动、关闭、类加载、线程、与操作...
深入理解Hotspot源码,有助于开发者优化Java应用,理解内存管理、垃圾收集的工作原理,以及如何利用JVM工具进行性能调优。对于Java程序员来说,这是一门必不可少的进阶课程,能提升代码编写和问题排查的效率,使应用...
【HotSpot GC官网文档截图 - 20200917】是一个珍贵的资源集合,包含了一系列关于Java HotSpot虚拟机(JVM)垃圾收集器(GC)的官方文档截图。这些截图详细介绍了GC的发展历程、不同版本的特性、选择GC的策略以及调优...
《性能工程师指南:玩转OpenJDK HotSpot垃圾收集器》是一本专注于Java应用程序性能优化的专业书籍,尤其针对OpenJDK HotSpot虚拟机的垃圾收集器进行了深入探讨。该书共83页,旨在帮助性能工程师理解并优化Java应用的...
5. **改进的垃圾收集**:JVM的垃圾收集器在Java 7中得到了优化,例如G1垃圾收集器的引入,提供了更高效的内存管理和更低的停顿时间。 6. **钻石操作符**:对于无参数的构造函数,现在可以省略泛型类型的尖括号,...
OpenJDK(HotSpot JVM、Javac)源代码学习研究(包括代码注释、文档、用于代码分析的测试用例)
- 类加载器负责加载类文件到JVM中,执行引擎执行字节码,垃圾收集器自动回收不再使用的内存,本地方法接口允许JVM调用非Java语言编写的库。 3. **运行时数据区**: - 包括程序计数器、虚拟机栈、本地方法栈、堆和...
### Java SE 6与HotSpot VM故障排除指南关键知识点解析 #### 一、概述 《Java SE 6与HotSpot VM故障排除指南》是Oracle公司于2008年11月发布的一份技术文档,旨在帮助开发人员、系统管理员和技术支持人员解决在...
以上内容概述了Java HotSpot虚拟机内存管理的核心概念、垃圾收集器的设计与选择、以及性能调优的建议和工具。对于Java开发和系统管理员来说,理解这些概念和工具对于编写高效、稳定的Java应用程序至关重要。
总结来说,Hotspot JVM作为Java平台的核心,其源码揭示了Java程序执行的底层细节,包括垃圾收集、内存管理、线程调度、编译优化等多个重要方面。通过深入学习Hotspot源码,开发者可以更好地理解和优化Java应用程序,...
性能工程师指南:玩转OpenJDK HotSpot垃圾收集器.ppt性能工程师指南:玩转OpenJDK HotSpot垃圾收集器.ppt性能工程师指南:玩转OpenJDK HotSpot垃圾收集器.ppt性能工程师指南:玩转OpenJDK HotSpot垃圾收集器.ppt性能...
本教程介绍了垃圾收集如何与Hotspot JVM一起使用的基础知识。了解垃圾收集器的工作原理后,学习如何使用Visual VM监视垃圾收集过程。最后,了解Java SE 7 Hotspot JVM中可用的垃圾收集器。
详细说明了J2SE 5.0版本中HotSpot JVM所实现的垃圾收集器类型,例如串行收集器、并行收集器、并行整理收集器和并发标记-清除(CMS)收集器等。每种收集器都有其特定的应用场景和性能特点。 4. 舒适性(Ergonomics)...
Java HotSpot虚拟机提供了垃圾收集器监控功能,用于实时监控垃圾收集器的活动信息。使用 `-XX:+UseGCLog`选项来启用垃圾收集器监控。 结论 Java HotSpot虚拟机提供了多种垃圾收集算法和调整选项,满足不同应用场景...
Java 11版本中的HotSpot虚拟机,与之前版本相比,引入了新的垃圾回收器(Epsilon垃圾回收器)、新的HTTP客户端API、支持新的HTTP/2协议等特性。 最后,文档强调,尽管Oracle的软件是为了普通用途的信息管理应用开发...
本指南主要涵盖了垃圾收集器的优化目标、策略以及各种实现,旨在帮助开发者更好地理解如何调整 JVM 参数以满足特定的需求。 优化目标与策略(Ergonomics) Java HotSpot VM 提供了两种主要的优化目标:最大暂停时间...
HotSpot后来成为了Java SE的标准JVM。 3. **Java 2 Platform, Standard Edition (J2SE)**:2000年,J2SE发布,引入了更多的JVM优化技术,如JIT编译器的进一步改进和内存管理的优化。 4. **Java 5.0与泛型**:2004...
3. 内存调优:通过调整新生代和老年代大小,设置最大和最小堆大小,以及垃圾收集器参数,实现内存的最佳利用。 四、性能监控与分析工具 1. JConsole和VisualVM:提供图形化界面监控JVM的内存、CPU、线程等状态。 2....