12.GC详解
垃圾回收:分代收集算法不同的区域使用不同的回收算法
Young代:GC频繁区域
Old代:GC次数较少
Perm代:不会产生GC!
一个对象的历程:
JVM在进行GC时,并非每次都对三个区域进行扫描的。大部分的时候都是指的新生代。
普通GC:只针对新生代【GC】
Full GC:主要是针对老年代,偶尔伴随新生代【Full GC】
1.JVM内存模型,每个区中存放什么?
2.堆中的分区:Eden,S from to,老年代,说说他们的特点。
3.GC的三种收集算法:标记清除,标记整理,复制算法,请你谈谈他的特点?
13.GC四大算法
引用计数法:
特点:每个对象都有一个引用计数器,每当对象被引用一次,计数器+1,如果引用失效,则计数器-1,如果为0,则GC可以清理。
缺点:
- 计数器维护麻烦
- 循环引用无法处理
JVM一般不采用这种方式(JVM一般采用可达性算法。)
复制算法
年轻代就是用的复制算法
1、一般普通GC之后,差不多Eden几乎都是空的了
2、每次存活的对象,都会被从from区和Eden区活着的对象复制到to区,这时from和to会发生一次交换:谁空谁是to,每当幸存一次,就会导致这个对象的年龄+1;如果这个年龄值大于15(默认值),就会进入养老区。
优点:没有标记和清楚的过程,效率高,没有内存碎片
缺点:to区永远是空的,需要浪费双倍的空间
Eden区,对象存活率极低。官方统计:99%的对象都会在使用一次之后,引用失效!推荐使用复制算法。
标记清除算法
老年代一般使用这个,但是会和我们后面的整理压缩一起使用。
优点:不需要额外的空间
缺点:两次扫描,耗时较为严重,会产生内存碎片,不连续。
标记整理算法
优化了标记清除算法的缺点,没有了内存碎片,但是耗时可能也较为严重。
如果空间中很少或者不经常发生GC,那么可以考虑使用标记整理算法。
小结:
内存效率:复制算法>标记清除算法>标记整理 (时间复杂度)
内存整齐度:复制算法=标记整理>标记清除
内存利用率:标记整理=标记清除>复制算法
年轻代:
Eden区:对象存活率极低,推荐使用复制算法
老年代:
区域比较大,对象存活率较高,推荐使用标记整理算法
14.JVM垃圾回收的时候如何确定垃圾,GC Roots
垃圾:简单说就是不再被引用的对象。
如果我们要进行垃圾回收,第一步:判断这个对象是否可以回收!
引用计数法
Java中,引用和对象都是有关联的,如果要操作对象,就要通过引用进行。
可达性分析算法:
一切都是从GC Roots这个对象开始遍历的,只要在这个树里面,就不是垃圾。
什么是GC Roots(4种)
1、虚拟机栈中引用的对象
2、类中的静态属性引用的对象
3、方法区中的常量
4、本地方法栈中的native方法引用的对象。
public class GCRoots{ // private byte[] array = new byte[100*1024*1024]; // 开辟内空间! // private static GCRoots2 t2; // GC root; // private static final GCRoots3 t3 = new GCRoots3(); // GC root; // 引用远远不止于此,强引用,软引用,弱引用,虚引用! 四个类的使用! public static void m1(){ GCRoots g1 = new GCRoots(); //GCroot System.gc(); } public static void main(String[] args){ m1(); } }
15. -X、-XX参数你用过哪些,JVM有哪些常用参数?
15.1 标配参数
在各种版本之间都很稳定,很少有变化。
java -version
java -help
java -showversion
...
15.2 X参数(了解)
java -Xint 解释执行
java -Xcomp 第一次使用就编译成本地的代码
java -Xmixed 混合模式(Java默认)
15.3 重点(XX参数boolean形)
语法:-XX:+(-) 某个属性值;+代表开启某个功能,-代表关闭某个功能。
开启PrintGCDetails功能后
15.4 XX参数之key=value形
元空间大小:jinfo -flag MetaspaceSize 进程号
-XX:MetaspaceSize=10m(一般有默认值,不需要修改)
jinfo -flag MaxTenuringThreshold 进程号(该参数主要是控制新生代需要经历多少次GC晋升到老年代中的最大阈值,在JVM中用4个bit存储(放在对象头中),所以其最大值是15
-XX:MaxTenuringThreshold=15
试图把值改为大于15的会出现如下错误:
查看所有默认值:
Non-default VM flags—默认的JVM参数
-XX:InitialHeapSize=268435456 默认初始堆大小
-XX:MaxHeapSize=4265607168 初始的最大内存
-XX:MaxNewSize=1421869056 最大的新生区大小
-XX:+UseParallelGC 并行GC垃圾回收器
JVM version is 25.191-b12
Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=268435456 - XX:MaxHeapSize=4265607168 -XX:MaxNewSize=1421869056 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=89128960 -XX:OldSize=179306496 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
Command line: -javaagent:D:\Dev_Soft\IDEA\lib\idea_rt.jar=64948:D:\Dev_Soft\IDEA\bin -Dfile.encoding=UTF-8
-Xms,-Xms是什么参数
1.-Xms:初始堆的大小;等价于:-XX:InitialHeapSize=268435456
2.-Xms:最大堆的大小;等价于:-XX:MaxHeapSize=4265607168
初始的默认参数到底有多少?
java -XX:+PrintFlagsInitial 查看Java环境初始默认值。这里面只要显示的值,我们都可以手动赋值,但不建议修改,了解即可。
=
默认值
:=
被修改过的值
java -XX:+PrintFlagsFinal -Xss128k ;查看被修改过的值,启动的时候判断
java -XX:+PrintCommandLineFlags -version;打印出用户手动设置的XX选项
16. 项目中常用的JVM参数有哪些
-Xms
-Xmx
-Xss 线程栈大小设置,默认512k—1024k
-Xmn 设置年轻代的大小,一般不动
-XX:MetaSpaceSize 设置元空间的大小,这个在本地内存中,一般不动
-XX:PrintGCDetails 打印垃圾回收信息
-XX:SurvivorRatio
设置新生代中的s0/s1空间的比例;
uintx SurvivorRatio = 8 代表:Eden:s0:s1=8:1:1
uintx SurvivorRatio = 4 代表:Eden:s0:s1=4:1:1
-XX:NewRatio 设置年轻代与老年代的占比
NewRatio = 2 代表:新生代是1,老年代是2,默认新生代整个堆的1/3;
-XX:MaxTenuringThreshold
进入老年区的存活阈值
MaxTenuringThreshold=15
17. OOM的认识
java.lang.StackOverflowError:栈溢出
public class OOMDemo { public static void main(String[] args) { a(); } public static void a(){ a(); } }
java.lang.OutOfMemoryError: Java heap space
public class OOMDemo { //-Xms10m -Xmx10m public static void main(String[] args) { String str = "aldksjflaksdjf"; while (true){ str += str + new Random(999999999) + new Random(999999999); } } }
java.lang.OutOfMemoryError: GC overhead limit exceeded
GC回收时间过长也会导致OOM;
可能CPU占用率一直是100%,GC但是没有什么效果。
public class OOMDemo { //-Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails public static void main(String[] args) { int i = 0; List<String> list = new ArrayList<>(); try { while (true){ list.add(String.valueOf(++i).intern()); } } catch (Throwable e) { System.out.println("i==>"+i); e.printStackTrace(); throw e; } } }
java.lang.OutOfMemoryError: Direct buffer memory 基础缓冲区的错误
public class OOMDemo { //-Xms10m -Xmx10m -XX:MaxDirectMemorySize=5 -XX:+PrintGCDetails public static void main(String[] args) throws InterruptedException { System.out.println("配置的MaxDirectMemorySize:"+VM.maxDirectMemory()/(double)1024/1024+"MB"); TimeUnit.SECONDS.sleep(2); // ByteBuffer.allocate():分配JVM的堆内存,属于GC管辖 // ByteBuffer.allocateDirect()分配本地OS内存,不属于GC管辖 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6*1024*1024); } }
java.lang.OutOfMemoryError: unable to create native Thread
高并发,unable to create native Thread 这个错误更多的时候和平台有关
1、应用创建的线程太多
2、服务器不允许你创建这么多线程。
public class TDemo { public static void main(String[] args) { for (int i = 1; ; i++) { System.out.println("i===>>>"+i); new Thread(()->{ try { Thread.sleep(Integer.MAX_VALUE); }catch (InterruptedException e){ e.printStackTrace(); } },String.valueOf(i)).start(); } } }
1、服务器线程不够了,超过了限制,也会爆出OOM异常。
java.lang.OutOfMemoryError: Metaspace 元空间报错
Java8之后使用元空间代替永久代,使用的是本地内存!
元空间里面存储的是:
1、虚拟机加载类信息
2、常量池
3、静态变量
4、编译后的代码
......
// -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m public class OomDemo { static class OOMTest{} public static void main(String[] args) throws Throwable { int i = 0; // 模拟计数器 try { while (true){ i++; // 不断的加载对象! Spring的 cglib; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMTest.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { return method.invoke(o,args); } }); enhancer.create(); } } catch (Throwable e) { System.out.println("i=>"+i); e.printStackTrace(); } } }
18. 请你谈谈垃圾收集器
GC算法(引用计数器,复制,标记清除,标记整理)方法论,垃圾收集器就是对应的落地实现。
4种垃圾收集器
1、串行垃圾回收器(STW:Stop The World)单线程
2、并行垃圾回收器(多线程工作,也会导致STW)
3、并发垃圾回收器(在回收垃圾的同时,可以正常执行线程,并行处理,但如果是单CPU,只能交替执行。)
4、G1垃圾回收器(将堆内存分割成不同的区域,然后并发的对其进行垃圾回收,Java9之后,默认使用G1)
垃圾回收器
查看默认的垃圾回收器
java -XX:+PrintCommandLineFlags -version
java的GC回收器主要有哪些?(以前7种,现在6种)
DefNew:默认的新一代【Serial串行】
Tenured:老年代【Serial Old】
ParNew:并行新一代【并行ParNew】
PSYoungGen:并行清楚年轻代【Parallel Scavcegn】
ParOldGen:并行老年区
Server/Client模式
默认现在都是Server模式;Client几乎不会使用
32位的Window操作系统,默认都是Client的JVM模式
64位的默认都是Server模式。
不同的垃圾回收器对应的机制不同。
串行GC
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseSerialGC
新生代:DefNew——老年代:Tenured
Serial——Serial Old
并行GC【不推荐使用】
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParNewGC
新生代:ParNew——老年代:Tenured
JDK8默认的GC
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseParallelGC
使用标准,如果选择垃圾回收器
1、单CPU,单机程序,内存小
-XX:+UseSerialGC
2、多CPU,单机程序,内存小
-XX:+UseParNewGC
3、多CPU,但是不希望有时间停顿,快速响应。
-XX:+UseParNewGC
或者-XX:+UseParallelGC
19. G1垃圾回收器
以前的垃圾回收器的特点
1、年轻代和老年代是各自独立的内存区域
2、年轻代使用Eden+s0+s1复制算法
3、老年代收集必须扫描整个老年代的区域
4、垃圾回收器原则:尽可能少而快的执行GC
G1垃圾回收器
G1(Garbage First)收集器;面向服务器端的应用的收集器。
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseG1GC
G1可以自定义垃圾回收的时间!
-XX:MaxGCPauseMillis=100
最大的GC停顿时间单位:毫秒,JVM尽可能的保证停顿小于这个时间!
G1优点
1、没有内存碎片
2、可以精准控制垃圾回收时间。
来源:https://www.cnblogs.com/tianxc/p/12493517.html