周志明先生所著的《深入理解Java虚拟机:JVM高级特性与最佳实践》(购买地址:亚马逊链接),对我学习Java、理解Java之道有非常大的帮助。至今已读过两遍,为了能够融会贯通,加深记忆(人老了记忆力差),便在Blog上记录一些认为该记的东西。
根搜索算法
堆中几乎存放着Java世界中所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象有哪些还“存活”这,哪些已经“死去”(即不可能再被任何途径使用的对象)。
主流的商用程序语言,都是使用根搜索算法判定对象是否存活着。这个算法的基本思想就是通过一系列的名为“GC Roots”的对象做起起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
在Java里,可作为GC Roots的对象包括下面几种:
1、虚拟机栈中的引用的对象。
2、方法区中的类静态属性引用的对象。
3、方法区中的常量引用的对象。
4、本地方法栈中JNI的引用的对象。
在JDK1.2之前,Java中的引用定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。
在JDK1.2之后,Java将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种,这四种引用强度依次逐渐减弱。
强引用就是指在程序代码中普片存在的,类似“Object obj=new Object()”这类引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
软引用用来描述一些还有用,但并非必需的对象。JVM在将要发生内存溢出前,会把这些对象回收。
弱引用也是用来描述非必需对象的。当垃圾收集器工作时,无论当前内存是否足够,弱引用对象都会被回收。
虚引用是最弱的一种引用关系。
垃圾收集算法
标记—清除算法 首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。该算法主要有两个缺点:一个是效率问题;另外一个是空间问题,会产生大量不连续的内存碎片。
复制算法 将可用内存分为两块,每次只是用其中的一块。当这一块的内存用完了,就把其中存活的对象复制到另外一块上面,然后再把已经使用过的内存空间一次清理掉。
现在商用的虚拟机都采用这种收集算法来回收新生代。在Hotspot中,jvm将内存分为一块较大的Eden空间和两块较小的Survivor空间(from space和to space),每次使用Eden和其中的一块survivor(from space)。当回收时,将还存活的对象一次性地拷贝到另外一块survivor空间(to space)上,最后清理掉Eden和刚才使用的Survivor空间。Hotspot默认Eden和Survivor的大小比例是8:1 。如果to space没有足够的空间存放上一次新生代收集后的存活对象,这些对象将直接进入老年代。
标记—整理算法 标记过程与“标记—清除算法”一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
分代收集算法 根据对象的存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法。而老年代中因为对象存活率搞、没有额外空间进行空间分配担保,就必须使用“标记—整理”或“标记—清除”算法。当前商业虚拟机一般都采用分代收集。
垃圾收集器
Hotspot虚拟机1.6版Update22的垃圾收集器如下图所示:
Serial收集器 这是一个单线程收集器。它进行垃圾收集时,必须暂停其他所有的工作线程(Stop The World),直到收集结束。它是虚拟机运行在Client模式下的默认新生代收集器。
ParNew收集器 ParNew其实就是Serial收集器的多线程版本,使用了多线程来进行垃圾收集。它是许多运行在Server模式下的虚拟机首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。
ParNew收集器在单CPU环境中不会比Serial收集器效率更高,甚至由于存在线程交互的开销,该收集器在通过超线程技术实现的两个CPU的环境中,都不能百分之百地保证能超越serial收集器。它默认开启的收集线程数与CPU的数量相同。
Parallel Scavenge收集器 该收集器也是新生代收集器,也是使用复制算法,也是并行的多线程收集器。它的目标是达到一个可控制的吞吐量。吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,如虚拟机共运行了100分钟,其中垃圾收集花了1分钟,则吞吐量就是(100-1)/100=99(%)。
老年代收集器:
Serial Old收集器 Serial Old收集器是Serial收集器的老年代版本,它也是个单线程收集器,使用“标记—整理”算法。是JVM在client模式下的默认老年代收集器。它在Server模式下有两大用途:一是在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用;另一个就是作为CMS收集器的后备预案,在CMS收集失败时,JVM会使用Serial Old进行收集。
Parallel Old收集器 Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记—整理”算法。
CMS收集器 CMS收集器使用“标记—清除”算法。它是“并发”(可以和用户线程“同时”工作)收集的,低停顿的收集器。主要有4个步骤:
1、初始标记
2、并发标记
3、重新标记
4、并发清除
初始标记和重新标记这两步骤仍然需要“stop the world”。初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快。并发标记阶段就是进行GC Roots Tracing过程,而重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。最后是耗时最长并发清除,收集器线程可以与用户线程一起工作。总体上来说,CMS收集器的内存回收过程与用户线程一起并发地执行。
CMS的缺点:
1、CMS在并发收集时,会与用户线程抢CPU资源……
2、CMS无法处理浮动垃圾。由于CMS在并发清理阶段,用户线程还在运行,自然会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,所以CMS无法在本次收集中处理它们,只能下一次GC时处理。由于CMS在收集阶段用户线程还需运行,JVM必须预留足够的内存空间给用户线程使用,所以,在默认设置下,CMS收集器在老年代使用了68%的空间后就会被激活。(可以通过参数来设置该百分比)
3、CMS基于“标记—清除”算法,则会产生空间碎片。可以通过参数开关来控制在FULL GC或若干次FULL GC后,进行碎片整理。为什么CMS不用“标记—整理”,可以参考这里:http://hllvm.group.iteye.com/group/topic/38223
G1收集器 G1(Garbage First)收集器是JDK1.7提供的一个新收集器,G1收集器基于“标记-整理”算法实现,也就是说不会产生内存碎片。还有一个特点之前的收集器进行收集的范围都是整个新生代或老年代,而G1将整个Java堆(包括新生代,老年代)。
垃圾收集器参数总结
参数名称 |
参数描述 |
UseSerialGC |
虚拟机运行在client模式下的默认值,打开次开关后,使用Serial+Serial Old 的收集器组合进行内存回收。 |
UseParNewGC |
打开次开关后,使用ParNew + Serial Old 的收集器组合进行内存回收。 |
UseConcMarkSweepGC |
打开次开关后,使用ParNew + CMS +Serial Old的收集器组合进行内存回收。Serial Old收集器将作为CMS收集器出现Concurrent Mode Failure失败收的后备收集器。 |
UseParallelGC |
虚拟机运行在Server模式下的默认值,打开次开关后,使用ParallelScavenge + Serial Old的收集器组合进行内存回收。 |
UseParallelOldGC |
打开次开关后,使用Parallel Scavenge + Parallel Old的收集器组合进行内存回收。 |
SurvivorRatio |
新生代中Eden区与Survivor区的容量比值,默认是8,代表Eden:Survivor = 8:1 |
PretenureSizeThreshold |
直接晋升到老年代对象的大小,设置这个参数后,大于这个参数的对象将直接在老年代分配。 |
MaxTenuringThreshold |
晋升到老年代的对象年龄。每个对象在坚持过一次Minor GC之后,年龄就加1,当超过这个参数值时就进入到老年代。 |
UseAdaptiveSizePolicy |
动态调整Java堆中各个区的大小以及进入老年代的年龄。 |
HandlePromotionFailue |
是否允许分配担保失败,即老年代空间不足以应对新生代的整个Eden和Survivor区的所有对象都存活的极端情况。 |
ParallelGCThreads |
设置并行GC时进行内存回收的线程数 |
GCTimeRatio |
GC时间占总时间的比率,默认值是99,仅在使用Parallel Scavenge收集器时生效。 |
MaxGCPauseMillis |
设置GC的最大停顿时间,仅在使用Parallel Scavenge收集器时生效。 |
CMSInitiatingOccupancyFraction |
设置CMS收集器在老年代空间被使用多少后触发垃圾收集。默认是68%,仅在使用CMS收集器时生效。 |
UseCMSCompactAtFullColletion |
设置CMS收集器在完成垃圾收集后是否必要进行一次内存碎片整理。仅在使用CMS收集器时生效。 |
CMSFullGCsBeforeCompaction |
设置CMS收集器在进行若干次垃圾收集后再启动一次内存碎片整理。仅在使用CMS收集器时生效。 |
-Xint |
禁用JIT编译。 |
-Xmn |
设置新生代的大小 |
-Xms |
设置初始堆的大小 |
-Xmx |
设置最大推的大小 |
-XX:PermSize |
设置永久代大初始大小 |
-XX:MaxPermSize |
设置永久代的最大值 |
-Xss |
设置虚拟机栈的大小。JDK1.5以前默认是256k,以后是1M |
-XX:+DisableExplicitGC |
禁用System.gc() |
相关推荐
《垃圾回收器与内存分配策略详解》 在Java编程中,理解垃圾回收(Garbage Collection,简称GC)机制和内存分配策略是至关重要的。GC的主要目的是自动管理内存,避免程序员手动进行繁琐且容易出错的内存释放工作。而...
《垃圾回收器与内存分配策略详解》 在Java编程中,理解垃圾回收(Garbage Collection,简称GC)机制和内存分配策略是至关重要的。GC的主要目的是自动管理内存,避免程序员手动进行繁琐且容易出错的内存释放工作。而...
Java虚拟机(JVM)是Java程序的核心组成部分,它负责执行字节码并管理内存。在JVM的学习中,理解其内存模型...在实际开发中,理解JVM的工作原理对于解决内存问题、选择合适的垃圾收集器和调整内存配置都具有重要意义。
内存分配与回收是编程语言...总的来说,理解Java的内存分配与回收策略对于优化程序性能和避免内存问题至关重要。开发者应关注GC的工作原理,合理设置内存参数,并善用不同类型的引用,以实现更高效、更稳定的程序运行。
- 分析垃圾收集器的行为。 - 避免不必要的对象创建。 - 优化算法和数据结构。 #### 九、类的封装与继承 类的设计在Java开发中至关重要。良好的封装不仅可以提升代码的安全性,还可以提高代码的可维护性和可扩展...
GC 日志用于记录垃圾收集器的执行情况,可以用于调试和优化垃圾收集器。 内存分配 Java 中的内存分配可以分为以下几种: * 栈上分配(Stack Allocation):在栈上分配内存。 * 堆上分配(Heap Allocation):在堆...
- **额外开销:** 每次内存分配和指针操作都需要额外的计数器更新,增加了系统的开销。 - **无法处理循环引用:** 如果两个或多个对象互相引用形成循环,则它们的引用计数永远不会归零,即使这些对象已经不再被使用...
Java虚拟机(JVM)是Java程序运行的基础,它的...开发者应根据应用的需求选择合适的JVM版本和垃圾回收策略,以实现最佳的系统性能。通过持续学习和实践,开发者可以更好地掌握JVM的奥秘,提升软件开发和维护的质量。
总结,JVM内存管理是Java开发者必备的知识,理解其内存结构和垃圾收集机制,结合合适的工具进行调优,能有效提高程序性能,避免出现因内存问题导致的系统不稳定。深入学习JVM内存管理,有助于成为一名优秀的Java...
2. 分代内存模型:理解新生代、老年代的概念,有助于优化内存分配策略。 3. 监控与诊断:使用JVisualVM、JProfiler等工具监控GC行为,找出性能瓶颈。 总之,Java垃圾回收是Java平台的一大特色,它极大地简化了内存...
2. CMS收集器与G1收集器:选择适合场景的垃圾收集策略。 3. Stop-the-world事件:降低其对应用的影响。 4. 内存分配策略:避免内存碎片,提高内存利用率。 四、线程与并发调优 1. 线程池配置:合理设置核心线程数...
- **标记-清除算法**:这是最基础的垃圾收集策略,分为标记和清除两个阶段,首先标记出所有存活的对象,然后清除未标记的对象。 - **复制算法**:为了优化标记-清除算法的效率,将内存分为两块,每次只使用一块,...
堆内存是Java程序中所有对象的存储区域,通过垃圾收集器进行管理,分为新生代和老年代,用于对象的生命周期管理。 #### 2.3 内存栈 栈内存存储方法的局部变量和方法调用的上下文信息,每个线程都有独立的栈。 ####...
比如,CMS(Concurrent Mark Sweep)收集器适合低暂停时间的需求,而G1(Garbage-First)收集器则提供了更为均衡的内存管理和响应时间。 优化JVM的GC性能通常涉及以下几个方面: 1. **合理设置堆大小**:根据应用...
- 垃圾收集器:如Serial、ParNew、Parallel Scavenge、CMS、G1等,用于自动回收不再使用的对象所占用的内存。 - 垃圾收集算法:标记-清除、复制、标记-整理和分代收集。 - 内存晋升策略:对象在新生代经过多次 ...
通过对Java堆和方法区的理解,结合不同的垃圾回收算法和垃圾回收器的选择,开发者可以更加灵活地调整和优化程序的运行性能。随着技术的发展,未来垃圾回收机制还将不断改进和完善,以适应更加复杂多变的应用需求。
垃圾收集算法主要用于释放那些不再使用的对象所占用的内存,从而避免内存泄漏问题。主要包括以下几种: - **标记-清除算法**(Mark-Sweep):首先标记所有需要回收的对象,然后统一回收这些标记过的对象。该算法的...
### Java基础复习笔记02:对象状态、引用种类、垃圾回收形式 #### 一、对象在内存中的状态 Java中的对象生命周期与...在实际开发中,应结合具体的应用场景选择合适的引用类型和垃圾回收策略,以达到最佳的运行效果。
不同的垃圾收集算法在性能、实时性、空间效率等方面有不同的表现,JVM通常提供了多种垃圾收集器供开发者根据应用需求选择和配置。例如,新生代和老年代可能采用不同的收集策略,以平衡吞吐量和响应时间。 总之,...
【华硕笔记本指纹收集器驱动】是专门为华硕品牌笔记本设计的一款硬件驱动程序,它主要服务于设备中的指纹识别模块。指纹解锁技术已经成为现代笔记本电脑安全防护的重要组成部分,为用户提供便捷且安全的身份验证方式...