JVM & GC 笔记


0. 说明

  转载并修改自JVM

 


 

1. JVM

  1.1 什么是JVM

  JVM为Java虚拟机(Java Virtual Machine)

 

  Runtime data area,运行时数据区。

  包含5个区域,分别为:

  method area         (方法区)

  heap                        (堆)

  java stack           (java栈)

  native method stack     (本地方法栈)

  program counter register。  (程序计数器)

 

  图示如下:

 

 

 

  1.2 method area(方法区)

  用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译后的代码等信息。

  方法区是线程间共享的,当两个线程同时需要加载一个类型时,只有一个类会请求ClassLoader加载,另一个线程会等待。

  1.3 heap(堆)

  虚拟机中用于存放对象与数组实例的地方,垃圾回收的主要区域就是这里(还可能有方法区)。

  如果垃圾收集算法采用按代收集(目前大都是这样),这部分还可以细分为新生代和老年代。新生代又可能分为Eden区,From Survivor区和To Survivor区,主要是为了垃圾回收。所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)。

  1.4 java stack(java栈)

  虚拟机栈也是线程私有的,每创建一个线程,虚拟机就会为这个线程创建一个虚拟机栈。

  虚拟机栈表示Java方法执行的内存模型,每调用一个方法,就会生成一个栈帧(Stack Frame)用于存储方法的本地变量表、操作栈、方法出口等信息,当这个方法执行完后,就会弹出相应的栈帧。

  栈帧分为三部分:局部变量区(Local Variables)、操作数栈(Operand Stack)和帧数据区(Frame Data)。

  如果请求的栈的深度过大,虚拟机可能会抛出StackOverflowError异常,如果虚拟机的实现中允许虚拟机栈动态扩展,当内存不足以扩展栈的时候,会抛出OutOfMemoryError异常。

1.5 native method stack(本地方法栈)

  与虚拟机栈类似,只是是执行本地方法时使用的。

1.6 program counter register。(程序计数器)

  类似于PC寄存器,是一块较小的内存区域,通过程序计数器中的值寻找要执行的指令的字节码,由于多线程间切换时要恢复每一个线程的当前执行位置,所以每个线程都有自己的程序计算器。这一个区域不会有OutOfMemeryError。当执行Java方法时,这里存储的执行的指令的地址,如果执行的是本地方法,这里的值是Undefined。

1.7 栈 & 堆 溢出

  栈溢出:Stackoverflow

  堆溢出:Out Of Memory

 


 

2. JVM从堆角度划分

  2.1 堆 & 非堆 & 离堆堆

   堆用来存放所有的对象和数组,在堆内空间又分为年轻代和年老代。

      • 年轻代

         年轻代分为伊甸区和幸存区。所有对象诞生于伊甸区,然后回收后经过幸存区。

        • 伊甸区

            所有对象诞生于伊甸区。

        • 幸存区

            之所以使用两个幸存区,是为了在每次回收对象后,可以进行内存碎片整理。以利用更有效使用内存。

            一区和二区也称为from区和to区,每次有一个区是空间是空的。但对象回收期间每个幸存区只经过一次。

          • 幸存一区
          • 幸存二区
      • 年老代

 

  • 非堆

   非堆是JVM内在堆外部分的内存,主要包含代码缓存区、压缩类空间和元数据区。JDK1.8之前称为永久区。

  • 离堆

   离堆是操作系统内JVM之外的内存空间。java可以直接操纵jvm之外的内存空间。

  2.2 参数调整

  jdk1.8之后,没有永久代的概念了,改成了元空间的叫法。而且官方文档上说不会再出现永久区的溢出问题,比如在使用maven进行编译时常常导致的问题。

 

参数解释 示例 备注
-Xss栈大小-Xss100m 
-Xms初始堆大小-Xms100m 
-Xmx最大堆设置-Xmx100m 
-Xmn年轻代设置-Xmn100m 
-XX:NewSize年轻代大小-XX:NewSize=100m 
-XX:MaxNewSize年轻代最大值-XX:MaxNewSize=100m 
-XX:NewRatio年老代是年轻代的倍数-XX:NewRatio=3,默认2 
-XX:SurvivorRatio伊甸区是单个幸存区的倍数-XX:SurvivorRatio=1,默认6 
-XX:MetaspaceSize元空间大小-XX:MetaspaceSize=1gnot work
-XX:MaxMetaspaceSize最大元空间 -XX:MaxMetaspaceSize=2gnot work
-XX:CompressedClassSpaceSize压缩类空间-XX:CompressedClassSpaceSize=2gnot work
-Xverbosegclog记录gc详细日志到文件-Xverbosegclog:/home/1.logWAS(Websphere application server)中使用
-Xloggc记录gc日志-Xloggc:/home/1.logJDK非标选项

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

3. JVM相关工具

  3.1 jvisualvm

  打开:Windows+R   -->    jvisualvm

  打开IDEA中运行程序,就能看到 jvisualvm 界面中的情况

  将所有区设为100M:

  -Xms400m -Xmx400m -Xmn300m -XX:SurvivorRatio=1

 

 

 

 

  3.2 jconsole

  打开IDEA中运行程序 

  打开:Windows+R   -->   jconsole

 

 

  3.3 jmap

 

  3.4 jstat

 

  • jstat查看帮助

 

 

  • 查看jstat选项

 

 

  • 查看jstat的gc情况

1秒钟1采样,采样100个样本,数字是进程id

 

 

  • 各字段含义

 

列名说明
S0C幸存一区容量(Survivor,Capacity,KB)
S1C幸存二区容量
S0U幸存一区使用的大小(Utility,KB)
S1U幸存二区使用的量
EC伊甸区容量
EU伊甸区使用量
OC年老代容量
OU年老代使用量
MC方法区容量
MU方法区使用量
CCSC压缩类空间容量
CCSU压缩类空间使用量
YGC年轻代gc次数
YGCT年轻代gc时间
FGCFull GC次数
FGCTFull GC时间
GCTGC总时间,FCCT + YGCT

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

4. GC

  4.1 什么是GC

  GC 垃圾回收(garbage collection)

  主要作用是回收程序中不再使用的内存

  为了减轻开发人员的工作,同时增加系统的安全性和稳定性,java语言提供了垃圾回收器来自动检测对象的作用域,可自动地把不再被使用的存储空间释放掉。主要的任务是:分配内存,回收不再被引用的对象的内存空间。

  对象回收的前提是没有任何引用能够直接或间接到达他。

 

  Java程序中显式将对象置为null是个不错的操作,起码保证了对象被回收的前提条件,但是显式调用System.gc()会显著降低系统性能。

  年轻代上发生的gc称为minor gc。java程序中,大部分的对象是“夭折”的,意思就是对象在minor gc的时候就被回收掉了,没有多少对象会进入到年老代。

  年老代上发生gc称为full gc或major gc,minor gc没有回收掉的对象会拷贝到年老代中。

  元空间(jdk1.8之前称为永久代),也就是方法区存放的是类常量以及字符串常量。该区域也会发生gc,并且这部分的gc也算作major gc。

 

  4.2 相关概念

  并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。

  并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行。而垃圾收集程序运行在另一个CPU上。

  4.3 GC类型

  • 1. Serial GC

  串行gc,配置参数为:   -XX:+UseSerialGC 。

  采用“mark-sweep-compact”算法,mark是标记依然存活的对象,sweep清理掉回收的对象,compact是压紧内存空间,可以理解为内存碎片整理。

  使用一个线程执行gc,串行gc很少使用。

 

  • 2. Parallel GC

  并行gc,配置参数为:  -XX:+UseParallelGC。

  串行gc使用一个线程执行gc,并行gc使用多个线程执行gc,因此在多核或是内存充足的情况下可以使用。串行gc很少使用。

 

  • 3. Parallel Old GC

  在JDK5之后才出现的算法,与并行gc算法的不是是针对年老代gc的算法采用的是“mark-summary-compact”。summary和sweep的不同是将gc之后幸存的对象放置到gc余弦处理好的不同区域,算法相对sweep来讲稍微复杂些。

  配置参数为:  -XX:+UseParallelOldGC

 

  • 4. CMS GC

  并行gc,该算法相较于之前的gc算法复杂得多。过程是“mark-sweep”阶段,没有了compact阶段。而且mark阶段又分成了initial mark和mark,其中initial mark需要stop-the-world,但是时间非常短,这一步主要是查找那些距离类加载器非常近的对象。之后的mark阶段是可以并行,即不需要stop-the-world,该步骤中,所有被幸存对象引用的对象会被确认是否已经被追踪和校验。remark阶段正如其名称一样,再一次检查那些在并行标记中增加或删除的对象,相当于验证过程。最后的并行sweep阶段开始执行gc过程,一旦采用该中gc,由gc导致的暂停时间非常短暂。因此CMS GC也叫低延迟gc,常用对响应时间非常苛刻的场景下。但CMS GC也有缺点:

    占用更多的内存和cpu

    默认不支持compact操作

  如果因为碎片过多,导致不得不执行compact操作时,stop-the-world时间要比其他任何gc都要长,需要考虑compact任务的发生频率和执行时间。

  配置参数为:  -XX:+UseConcMarkSweepGC

 

  • 5. G1 GC

  G1类型是垃圾回收优先类型,在jdk1.7才正式发布的一个算法。G1的结构如图:

  

  在该类型下,不再有年轻代和年老代的概念。如图所示,每个对象被分配到不同的格子,随后执行gc。一个区域装满后,对象被分配到另一个区域,并执行gc。中间不再有从年轻代到年老代转义的三个步骤了。该类型为替代CMS类型而创建,因为CMS在长时间持续运行时导致很多问题。

  配置参数为:  -XX:+UseG1C

G1的最大好处是性能,他比任何一种GC都快,但是一定要在成熟的jdk版本上使用它。

 

总结

收集器串行、并行or并发新生代/老年代算法目标适用场景
Serial串行新生代复制算法响应速度优先单CPU环境下的Client模式
Parallel并行新生代复制算法吞吐量优先在后台运算而不需要太多交互的任务
Parallel Old并行老年代标记-整理吞吐量优先在后台运算而不需要太多交互的任务
CMS并发老年代标记-清除响应速度优先集中在互联网站或B/S系统服务端上的Java应用
G1并发both标记-整理+复制算法响应速度优先面向服务端应用,将来替换CMS

 

 

 

 

 

 

 

 

 

  4.4 Demo

  Hello类

 1 class Hello 
 2 {
 3     public static void main(String[] args) throws Exception
 4     {
 5                 //指定循环次数  
 6         int count = Integer.parseInt(args[0]) ;
 7         //指定byte数组大小
 8                 int size = Integer.parseInt(args[1]) ;
 9         for (int i  = 0 ; i<= count ;  i++) {
10             byte[] bytes = new byte[size] ;
11         }
12     }
13 }     

 

测试类中的 testProcess() 方法,测试5种垃圾回收类型所需要的时间

 1 @Test
 2 public void testProcess() throws Exception {
 3     //5中gc算法
 4     String[] gcs = {
 5         "UseSerialGC" ,
 6         "UseParallelGC" ,
 7         "UseParallelOldGC" ,
 8         "UseConcMarkSweepGC" ,
 9         "UseG1C"
10     } ;
11     
12     Runtime r = Runtime.getRuntime();
13 
14     for(String gc  :gcs){
15         System.out.print(gc + "\t: ");
16        for(int i = 0 ; i < 3 ; i ++){
17             String javapc = String.format("java -Xms500m -Xmx500m -XX:NewSize=7m -XX:MaxNewSize=7m -XX:SurvivorRatio=5 -XX:+%s -cp d:/java Hello 10000 6000000" , gc) ;
18             long start = System.nanoTime() ;
19             Process p = r.exec(javapc);
20             p.waitFor();
21             System.out.print((System.nanoTime() - start) + "\t");
22         }
23     System.out.println();
24 }

 

 

  4.5 GC过程

  GC 的回收过程是伊甸区已满,向幸存区的to区回收,同时幸存区的from区也会向to进行回收,注意幸存区有两个,同一时刻只有一个幸存区是空的,from区和to区会交替进行角色交换,但是对象在回收过程中只经过一次从from到to的过程,如果对象仍没有回收掉,就会进入到年老代。

  

 

  4.6 GC优化

  GC时,除了 GC 所需要的线程外,app的所有线程都会暂停,直到 GC 过程结束。因此有stop-the-world一次的说法。

  对 GC 的优化,很多时候也是指要减少stop-the-world的时间。

 

 


 

转载于:https://www.cnblogs.com/share23/p/9643094.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值