1.java虚拟机运行时数据区域
Java虚拟机在执行Java程序过程中,会把它所管理的内存分为若干个不同的数据区域。根据《Java虚拟机规范》的规定,其所管理的内存将会包括以下几个运行时数据区域。
1.1 程序计数器
程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,通过改变程序计数器的值,来选取将要执行的字节码指令。在多线程环境下,各个线程通过轮流切换、分配处理器时间来执行,因此每个线程需要一个独立的计数器,以便线程切换回来后,能够知道程序执行的位置。此内存区域是java虚拟机中唯一一个不会出现OOM的区域。
如果线程正在执行的是一个Java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器的值则应该为空(Undefined)
1.2 虚拟机栈
栈是一种特殊的线性表,仅能在线性表的一端进行操作,称为入栈和出栈。
虚拟机栈描述的是Java方法执行的内存模型,每个方法被调用的时候,Java虚拟机就会同步创建一个栈帧压入栈中,每一个方法执行完闭就对应着一个栈帧出栈。可以思考一下,为什么递归调用容易出现StackOverflowError异常。
调用Java方法时虚拟机会同步生成栈帧,那么栈帧中存放哪些信息呢?
- 局部变量表(Local Variables)
- 操作数栈(Opreand Stack)
- 动态链接 (Dynamic Linking) (指向运行时常量的方法引用)
- 方法出口(方法调用完成)分为常规方法调用完成 ;突然方法调用完成
- 一些附加信息
第三章Jvm内存分配与垃圾收集
** 问题 **
1.java虚拟机如何分配内存(内存分配原则)
2.垃圾收集主要针对哪些区域
3.何时会触发GC
4.触发GC时虚拟机怎样判断哪些对象存活,哪些对象已经死亡
5.垃圾收集算法有哪些
6.介绍下主流的虚拟机中包含哪些垃圾收集器
7.HotSpot虚拟机垃圾收集算法细节
JVM主流垃圾收集器
Serial( [‘sɪəriəl] )(串行)收集器,Serial Old收集器
如图,Serial收集器是一个单线程工作的收集器,新生代采用标记-复制算法,老年代采用标记-整理算法,垃圾收集时需要暂停所有用户线程。
Serrial收集器是HotSpot虚拟机运行在客户端模式下的默认收集器,有着优于其他收集器的地方,那就是简单而高效(与其他收集器的单线程相比),对于内存资源受限的环境,它是所有收集器里额外内存消耗最小的;对于单核处理器或处理器核心数较少的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
ParNew 收集器
ParNew收集器实际上是Serial收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一致。
ParNew收集器,在单核处理器的环境中绝对不会有比Serial收集器更好的效果,但是随着可以被使用的处理器核心数量的增加,ParNew对于垃圾收集时系统资源的高效利用还是很有好处的。它默认开启的收集线程数与处理器核心数量相同,在处理器核心非常多(譬如32个,现在CPU都是多核加超线程设计,服务器达到或超过32个逻辑核心的情况非常普遍)的环境中,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。
Parallel Scavenge收集器
Parallel Old收集器
CMS收集器
CM收集器执行过程
CMS(Concurrent Mark Sweep)收集器是一种以获取最短停顿时间为目标的垃圾收集器。它是基于标记——清除算法实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为四个步骤:
1.初始标记(Stop the World)
2.并发标记
3.重新标记(Stop the World)
4.并发清除
初始标记仅仅是标记GC Roots能直接关联到的对象,速度很快;并发标记就是从GC Roots的直接关联对象开始遍历整个对象图,这个过程耗时较长但是不需要停顿用户线程。
而重新标记阶段则是为了修正并发标记阶段用户线程对对象图引用关系的影响。
补充
增量式并发收集器(Incremental Concurrent Mark Sweep/i-CMS)
首先,CMS收集器对处理器资源非常敏感。在并发阶段,它虽然不会导致用户线程停顿,但却会因为占用了一部分线程(或者说处理器的资源),而导致应用程序变慢,降级总吞吐量。CMS默认启动的回收线程数是**(处理器核心线程数+3)/4** ,也就是说,如果处理器核心数在四个或以上,并发回收时垃圾收集线程只占用不超过25%的处理器运算资源,并且会随着处理器核心数量的增加而下降。但是当处理器核心数量不足四个时,CMS对用户程序的影响就可能变得很大。如果应用本来的处理器负载就很高,还要分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然大幅降低。为了缓解这种情况,虚拟机提供了一种称为“增量式并发收集器”(Incremental Concurrent Mark Sweep/i-CMS)的CMS收集器变种,所做的事情和以前单核处理器年代PC机操作系统靠抢占式多任务来模拟多核并行多任务的思想一样,是在并发标记、清理的时候让收集器线程、用户线程交替运行,尽量减少垃圾收集线程的独占资源的时间,这样整个垃圾收集的过程会更长,但对用户程序的影响就会显得较少一些,直观感受是速度变慢的时间更多了,但速度下降幅度就没有那么明显。实践证明增量式的CMS收集器效果很一般,从JDK 7开始,i-CMS模式已经被声明为“deprecated”,即已过时不再提倡用户使用,到JDK 9发布后i-CMS模式被完全废弃。
虚拟机参数说明:
-XX:CMSInitiatingOccupancyFraction
设置CMS收集器在老年代使用了多少空间后(百分比)触发垃圾收集操作,JDK5默认设置为68%,JDK6默认阈值为92%。
-XX:+UseCMS-CompactAtFullCollection (默认是开启的,此参数从JDK 9开始废弃)
用于在CMS收集器不得不进行Full GC时开启内存碎片的合并整理过程。
-XX:CMSFullGCsBefore-Compaction (此参数从JDK 9开始废弃)
这个参数的作用是要求CMS收集器在执行过若干次(数量由参数值决定)不整理空间的Full GC之后,下一次进入Full GC前会先进行碎片整理(默认值为0,表示每次进入Full GC时都进行碎片整理)。