我们写java代码的时候,业务逻辑处理里面会产生很多对象,这些对象在调用结束后jvm的垃圾回收器GC就帮我们回收处理了,但是具体的逻辑是如何处理的,如何如优化。
1、什么是垃圾?
简单来说就是:
没有任何引用指向这个对象,那这个对象对jvm来说就是垃圾,需要回收。
2、jvm如何定位到这些垃圾?使用的什么算法
jvm定位垃圾的算法有两种:
1:引用计数器
顾名思义,就是通过计数来判断这个对象是否被引用。
当一个对象有引用时计数器+1,引用消失时计数器-1,当计数器为0的时候,对象不在被引用,也就标记为垃圾。这种算法比较简单,通过计数器操作,但是有一个致命的问题就是循环引用,对象A引用了对象B,B也引用了A,但是A和B没有被第三个对象应用。
2:根可达算法
这个也很好理解,就是从根开始往下面捋,通过根能引用到的就是有用的,剩下引用不到的就是垃圾。也就是先找到有用的对象,剩下的就是没用的。通常用GC Roots作为起点。
3、常见的垃圾回收算法
1:标记清除
这里分为两个阶段:标记和清除,在jvm定位垃圾的时候对对象进行标记,标记完成后统一回收这些被标记为垃圾的对象。
缺点:
1、效率不高
2、标记位置不连续,容易产生内存碎片
2:拷贝算法
拷贝算法是将内存分为相同的两块区域,每次使用其中一块,当这块内存使用完了,就将其中还存活的对象负责到另外一块内存空间,当前的这块清空,没有碎片产生。
缺点:每次是使用了一半的内存,内存空间没有有效利用,浪费了空间
3:标记压缩
这个和标记清除比较类似,标记的过程和标记清除一样,但是标记完成后,不是对垃圾进行回收,而是把正常存活的对象压缩到内存的一段,然后将这个边界外的全部清除。
4、JVM堆内存分代模型
1、java堆是所有线程共享的,是java虚拟机管理的内存中最大的一块,堆内存在虚拟机启动时创建。
将堆内存分为几块,分别对应新生代、老年代、永久代;
2、新生代:刚new出来的对象放入新生代,垃圾回收一次后还存活的会放入幸存区survive;(新生代包括Eden区、From Survivor区、To Survivor区,系统默认大小Eden:Survivor=8:1)。
3、老年代:新new的很大的对象和survive幸存区回收好多次没有回收掉的,进入老年代,老年代存放的是生命周期比较长的对象,老年代进行FULL GC。
对象超过多大直接进入老年代可以通过参数设置:
-XX:PretenureSizeThreshold
幸存区经历多少次GC后进入老年代也可以设置:
XX:MaxTenuringThreshold
4、永久代/元空间:存放class对象(jdk1.8之后叫Metaspace元空间,元空间的本质和永久代类似,都是对JVM规范中方法区的实现。)
区别:
1、元空间并不在虚拟机中,而是使用本地内存;
2、永久代必须设置内存大小,元空间可以设置也可以不设置,受限物理内存
堆内存逻辑分区:
STW:Stop the Word,当JVM进行FULL GC的时候会产生。
5、JVM虚拟机栈
1、java虚拟机栈是线程私有的,生命周期和线程相同;
2、每个方法被调用到完成的过程,都对应一个栈帧在虚拟机栈中的入栈和出栈的过程;
3、虚拟机栈是执行java方法的内存模型服务,在每一个方法执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
6、方法区
可通过参数-XX:MaxPermSize设置
1、方法区是所有线程共享的内存区域,用于存储已被虚拟机加载的类信息,常量,静态变量;
2、方法区存放java类定义的信息,和垃圾回收关系不大,可以设置不实现垃圾回收,但是不是说没有垃圾回收;
3、方法区域的内存主要回收目标是针对常量池的回收和类型的卸载;
4、运行时常量池也是方法区的一部分,虚拟机加载Class后把常量池的数据放入运行时常量池;
7、常用JVM参数设置
参数 | 说明 | 实例 |
---|---|---|
-Xms | 初始堆大小,默认物理内存的1/64 | -Xms512M |
-Xmx | 最大堆大小,默认物理内存的1/4 | -Xmx2G |
-Xmn | 新生代内存大小,官方推荐堆内存的3/8 | -Xmn512M |
-Xss | 线程堆栈大小,jdk1.5之后默认1M | -Xss512K |
-XX:NewRatio=n | 设置新生代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4 | -XX:NewRatio=3 |
-XX:SurvivorRatio=n | 年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1:1,一个Survivor区占整个年轻代的1/8 | -XX:SurvivorRatio=8 |
-XX:PermSize=n | 永久代初始值,默认为物理内存的1/64 | -XX:PermSize=128M |
-XX:MaxPermSize=n | 永久代最大值,默认为物理内存的1/4 | -XX:MaxPermSize=256M |
-verbose:class | 在控制台打印类加载信息 | |
-verbose:gc | 在控制台打印垃圾回收日志 | |
-XX:+PrintGC | 打印GC日志,内容简单 | |
-XX:+PrintGCDetails | 打印GC日志,内容详细 | |
-XX:+PrintGCDateStamps | 在GC日志中添加时间戳 | |
-Xloggc:filename | 指定gc日志路径 | -Xloggc:/data/jvm/gc.log |
-XX:+UseSerialGC | 年轻代设置串行收集器Serial | |
-XX:+UseParallelGC | 年轻代设置并行收集器Parallel Scavenge | |
-XX:ParallelGCThreads=n | 设置Parallel Scavenge收集时使用的CPU数。并行收集线程数 | -XX:ParallelGCThreads=4 |
-XX:MaxGCPauseMillis=n | 设置Parallel Scavenge回收的最大时间(毫秒) | -XX:MaxGCPauseMillis=100 |
-XX:GCTimeRatio=n | 设置Parallel Scavenge垃圾回收时间占程序运行时间的百分比。公式为1/(1+n) | -XX:GCTimeRatio=19 |
-XX:+UseParallelOldGC | 设置老年代为并行收集器ParallelOld收集器 | |
-XX:+UseConcMarkSweepGC | 设置老年代并发收集器CMS | |
-XX:+CMSIncrementalMode | 设置CMS收集器为增量模式,适用于单CPU情况 |