笔记:JVM

JVM

类似面试题:

谈谈你对JVM理解,java8虚拟机有什么更新
什么是OOM,什么是StackOverFlowError,有哪些分析方法
JVM的常用参数调优你知道哪些
谈谈JVM中,对类加载器的认识

JVM体系结构概述

在这里插入图片描述
灰色:线程私有,不存在垃圾回收
亮色:线程共享,存在垃圾回收

类加载器ClassLoader

负责加载class文件,class文件在文件开头有特定的文件标识(cafe babe),将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构并且ClassLoader只负责class文件的加载,至于它是否可以运行则由Execution Engine决定。

方法去:放类的模板的地方

在这里插入图片描述

类加载器分类

虚拟机自带的加载器:

启动类加载器(Bootstrap) C++编写 =》object.getClass.getClassLoader =>null (rt.jar包中的类被启动时就被BootStrap加载了)
扩展类加载器(Extension) Java 编写 =》后面添加的扩展的 javax的包
应用程序类加载器(AppClassLoader):一般我们用的

用户自定义类加载器:

Java.lang.ClassLoader的子类,用户可以定制类的加载方式

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

ClassLoader双亲委派机制

一个类加载器收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一层类加载器都是如此,因此所有的加载请求都应该传送到启动类加载器中,只有当父类加载器反馈自己无法完成这个请求时,(在它加载的路径下没有找到所需加载的Class),子类加载器才会尝试自己加载。(如果自己创建一个lang包下mainString类,会报错)
好处:

比如加载位于rt.jar包中的类java.lang.Object,不管是哪个加载器加载这个类,最终都是托付给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载器最终得到的都是同一个Object对象。
沙箱安全:防止你的代码恶意污染源代码

本地接口

native关键字:表示调用与Java无关的底层函数库

Native Method Stack(本地方法栈)

它的具体做法是Native Method Stack中登记native方法,在Execution Engine 执行时加载本地方法库。

PC寄存器

每个线程中都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码 (用来存储指向下一条命令的地址,即将要执行的指令代码) ,由执行引擎读取下一条命令,PC寄存器是一个非常小的存储空间,几乎可以忽略不记。是当前线程所指向字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码命令。如果执行的是一个Native方法,那么这个计数器是空的。
用已完成分支、循环、转跳、异常处理、线程恢复等基础功能,不会发生内存溢出错误。

方法区

供各线程共享的运行时内存区域。它存储了每一个类的结构信息(模板(大的Class)),例如运行时常量池、字段和方法数据、构造函数和普通方法的字节码内容。上面讲的是规范,在不同虚拟机里头实现是不一样的,最典型的就是永久代(PermGen space)和元空间(Metaspace)。
永久代元空间对应方法区:JVM规范将方法区描述为堆的一个逻辑部分,但是实际上方法区还有一个别名:非堆内存,永久代来实现方法区(类似接口和类的关系)永久代几乎没有垃圾回收,用于存放JDK自带的Class、Interface的元数据。关闭JVM时才会释放这块内存。
但是,实例变量(对象)存在于堆内存中,和方法区无关。

栈管运行,堆管存储
栈也叫栈内存,主管程序Java运行,是在线程创建时创建,它的生命周期是跟随线程的生命期,线程结束栈内存也就释放了。对于栈来说不存在垃圾回收问题,只要线程一结束该栈就Over,生命周期和线程都一致,是线程私有的。8种基本类型的变量+对象引用变量+实例方法都是在函数的栈内存中分配。

栈帧存储什么

栈帧中主要保存3类数据:可以这样想(栈帧就是Java方法)
本地变量:输入参数和输出参数以及方法内的变量;
栈操作:记录出栈、入栈的操作;
栈帧数据:包括类文件、方法等;

栈运行原理

栈中数据都是以栈帧的格式存在的,栈帧是一个内存区块,是一个有关方法的运行期数据的数据集,当一个方法A被调用时就产生了一个栈帧F1,并被压入到栈中,A方法调用了B方法,于是又产生栈帧F2被压入栈…
执行完毕后,先弹出F2在弹出F1…
每个方法执行的同时都会创建一个栈帧,用于存储去不变量表、操作数栈、动态来凝结、方法出口信息等每一个方法从调用一直到执行完毕的过程,就对应着一个栈帧在虚拟机中从入栈到出栈的过程。栈的大小和具体JVM实现有关,通常在256K、756K之间,约等于1MB

public class TestError {
    // 栈帧过多,导致栈满
    // Exception in thread "main" java.lang.StackOverflowError
    
    public static void m1(){
        m1();
    }
    public static void main(String[] args) {
        m1();
    }
}

Error是个错误

栈、堆、方法区的交互关系

在这里插入图片描述
图片描述:
referrnce表示引用,例如:Student student = new Student();
对象实例数据就表示上述student(可以有多个对象示例数据)
对象类型数据表示Student.Class

HotSpot(JDK)是使用指针的方式来访问对象
Java堆中会存放访问类元数据(模板,Class文件) 的地址,而reference储存的就直接是对象的地址。

堆体系结构概述

一个JVM实例只存在一个堆内存,堆内存的大小是可以调节的,类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三个部分:
Young Generation Space 新生区 Young/New
Tenure Generation Space 养老区 Old/Tenure
Permanent Space 永久区 Perm
Java7之前堆结构
在这里插入图片描述
Java7之后(Java8)永久储存区变为元空间
物理上两个组成:新生+养老
在这里插入图片描述

Heap堆new内存溢出

假设一致while(true){创建新对象}
那么,所有的new对象先到伊甸区,直到到达伊甸区阈值,伊甸区会进行一次YGC(Young)=》伊甸区对象基本清空(有幸存的进入幸存者0区)
幸存者0区=S0区被称为from区
幸存者1区=S1区被称为to区
from区和to区他们的位置和名分不是固定的,每次GC后会交换,谁空谁就是to区
经过多次(默认15次)YGC的对象进入养老区
因为一直都在new对象,养老区也会变满,当到达阈值后,会进行Full GC,
FullGC多次后发现养老区还是没办法腾出来,会报出异常OOM

1.eden、from复制到to,年龄+1:
首先,当Eden区满时,会触发第一次GC,把还活着的对象拷贝到From区,当Eden区再次满触发GC时,会扫描Eden和From区,对两个区进行垃圾回收,经过这次GC还活着的对象,则直接复制到To区(如果这时有对象年龄到达老年标准,则复制到老年区),同时,将这些对象年龄+1
2.清空eden、from
然后,清空Eden和from中所有对象,之后将to和from交换,谁空谁是to
3.to和from交换
to和from交换,原to区称为下一次GC的from区,部分对象会在from和to区中复制来复制去,如此交换15次之后(JVM参数MaxTenuring Threshold决定,默认15)如果还活着,就存入老年区。

方法区和堆一样,是各个线程共享的内存区域,它用于虚拟机加载的类信息+普通常量+静态方法+编译器编译后的代码等,虽然JVM规范将方法区描述成堆的一个逻辑区域,但他还有一个别名:Non-Heap(非堆),目的是要将方法区和堆分开。
对于HotSpot虚拟机,很多开发者习惯将方法区称为“永久代”,但本质上来讲两者本质不同,但严格意义上两者不同。或者是使用永久代来实现方法区。
永久区(元空间)是一个常驻内存区域,用于存放JDK自身所携带的Class,Interface的元数据,也就是说它存储的是运行环境必须的信息,此区域的数据是不会被垃圾回收器回收的,关闭JVM才会释放此区域所占的内存。

堆参数调优入门

java7
在这里插入图片描述
java8
在这里插入图片描述

-Xms堆初始化大小,默认物理内存的1/64
-Xmx堆最大化大小,默认物理内存的1/4
-Xmn new区大小
-XX:+PrintGCDetails 输出详细的GC处理日志

在Java8中,永久代已经被移除,被一个称为元空间的区域所取代,原空间的本质和永久代类似。区别在于
永久代使用的JVM的堆内存,但是在java8之后的元空间并不在虚拟机中,而是使用本机物理内存
因此,默认情况下,圆孔家的大小尽受本地内存限制。类的元数据可放入native memory,字符串池和类的静态变量放入java堆中,这样可以加载多少类的元数据不再受MaxPermSize控制,而是由系统实际可用空间来控制

public class T2 {
    public static void main(String[] args) {
        //Runtime即为RunTime Data Area的对象
        System.out.println(Runtime.getRuntime().availableProcessors());//返回核数
        long maxMemory = Runtime.getRuntime().maxMemory() ;//返回 Java 虚拟机试图使用的最大内存量。
        long totalMemory = Runtime.getRuntime().totalMemory() ;//返回 Java 虚拟机中的内存总量。
        System.out.println("-Xmm:MAX_MEMORY = " + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB");
        System.out.println("-Xms:TOTAL_MEMORY = " + totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB");

    }
JVM内存配置

开发中,通常将-Xms和-Xmm设置为相同的大小,避免因为内存收缩/突然增大带来性能影响
在idea中
在这里插入图片描述
设置为1G,并且输出详细GC处理日志
在这里插入图片描述
接下来,为了查看OOM异常,我们将Xms和Xmm全改为10m
写入代码:

import java.util.Random;

public class T2 {
    public static void main(String[] args) {
        /*//Runtime即为RunTime Data Area的对象
        System.out.println(Runtime.getRuntime().availableProcessors());//返回核数
        long maxMemory = Runtime.getRuntime().maxMemory() ;//返回 Java 虚拟机试图使用的最大内存量。
        long totalMemory = Runtime.getRuntime().totalMemory() ;//返回 Java 虚拟机中的内存总量。
        System.out.println("-Xmm:MAX_MEMORY = " + maxMemory + "(字节)、" + (maxMemory / (double)1024 / 1024) + "MB");
        System.out.println("-Xms:TOTAL_MEMORY = " + totalMemory + "(字节)、" + (totalMemory / (double)1024 / 1024) + "MB");
        */
        String str = "yyx";
        while (true){
            str += str + new Random().nextInt(88888888) + new Random().nextInt(99999999);
        }
    }
}

运行结果:
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
at java.base/jdk.internal.misc.Unsafe.allocateUninitializedArray0(Unsafe.java:1276)
at java.base/jdk.internal.misc.Unsafe.allocateUninitializedArray(Unsafe.java:1269)
日志结果:
无法使用查看详细信息日志,只能用-Xlog:gc*

“C:\Program Files\Java\jdk-9.0.4\bin\java.exe” -Xms10m -Xmx10m -Xlog:gc* “-javaagent:D:\IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=55185:D:\IDEA\IntelliJ IDEA 2019.3.3\bin” -Dfile.encoding=UTF-8 -classpath D:\WorkSpace\JVMNote\out\production\JVMNote com.yyx.runtest.T2
[0.024s][info][gc,heap] Heap region size: 1M
[0.026s][info][gc ] Using G1
[0.026s][info][gc,heap,coops] Heap address: 0x00000000ff600000, size: 10 MB, Compressed Oops mode: 32-bit
[0.207s][info][gc,start ] GC(0) Pause Young (G1 Evacuation Pause)
[0.207s][info][gc,task ] GC(0) Using 8 workers of 8 for evacuation
[0.208s][info][gc,phases ] GC(0) Pre Evacuate Collection Set: 0.0ms
[0.208s][info][gc,phases ] GC(0) Evacuate Collection Set: 1.5ms
[0.208s][info][gc,phases ] GC(0) Post Evacuate Collection Set: 0.1ms
[0.209s][info][gc,phases ] GC(0) Other: 0.1ms
[0.209s][info][gc,heap ] GC(0) Eden regions: 4->0(2)
[0.209s][info][gc,heap ] GC(0) Survivor regions: 0->1(1)
[0.209s][info][gc,heap ] GC(0) Old regions: 0->1
[0.209s][info][gc,heap ] GC(0) Humongous regions: 0->0
[0.209s][info][gc,metaspace ] GC(0) Metaspace: 6265K->6265K(1056768K)
[0.209s][info][gc ] GC(0) Pause Young (G1 Evacuation Pause) 4M->1M(10M) 1.745ms
[0.209s][info][gc,cpu ] GC(0) User=0.00s Sys=0.00s Real=0.00s
[0.220s][info][gc,start ] GC(1) Pause Initial Mark (G1 Humongous Allocation)
[0.220s][info][gc,task ] GC(1) Using 8 workers of 8 for evacuation
[0.222s][info][gc,phases ] GC(1) Pre Evacuate Collection Set: 0.0ms
[0.222s][info][gc,phases ] GC(1) Evacuate Collection Set: 1.5ms
[0.222s][info][gc,phases ] GC(1) Post Evacuate Collection Set: 0.1ms
[0.222s][info][gc,phases ] GC(1) Other: 0.1ms
[0.222s][info][gc,heap ] GC(1) Eden regions: 2->0(1)
[0.222s][info][gc,heap ] GC(1) Survivor regions: 1->1(1)
[0.222s][info][gc,heap ] GC(1) Old regions: 1->2
[0.222s][info][gc,heap ] GC(1) Humongous regions: 3->2
[0.222s][info][gc,metaspace ] GC(1) Metaspace: 6347K->6347K(1056768K)
[0.222s][info][gc ] GC(1) Pause Initial Mark (G1 Humongous Allocation) 5M->3M(10M) 1.733ms
[0.222s][info][gc,cpu ] GC(1) User=0.00s Sys=0.00s Real=0.00s
[0.222s][info][gc ] GC(2) Concurrent Cycle
[0.222s][info][gc,marking ] GC(2) Concurrent Clear Claimed Marks
[0.222s][info][gc,marking ] GC(2) Concurrent Clear Claimed Marks 0.013ms
[0.222s][info][gc,marking ] GC(2) Concurrent Scan Root Regions
[0.222s][info][gc,marking ] GC(2) Concurrent Scan Root Regions 0.182ms
[0.222s][info][gc,marking ] GC(2) Concurrent Mark (0.222s)
[0.222s][info][gc,marking ] GC(2) Concurrent Mark From Roots
[0.222s][info][gc,start ] GC(3) Pause Young (G1 Humongous Allocation)
[0.222s][info][gc,task ] GC(2) Using 2 workers of 2 for marking
[0.222s][info][gc,task ] GC(3) Using 8 workers of 8 for evacuation
[0.223s][info][gc,phases ] GC(3) Pre Evacuate Collection Set: 0.2ms
[0.223s][info][gc,phases ] GC(3) Evacuate Collection Set: 0.5ms
[0.223s][info][gc,phases ] GC(3) Post Evacuate Collection Set: 0.1ms
[0.223s][info][gc,phases ] GC(3) Other: 0.1ms
[0.223s][info][gc,heap ] GC(3) Eden regions: 0->0(1)
[0.223s][info][gc,heap ] GC(3) Survivor regions: 1->1(1)
[0.223s][info][gc,heap ] GC(3) Old regions: 2->2
[0.223s][info][gc,heap ] GC(3) Humongous regions: 2->2
[0.223s][info][gc,metaspace ] GC(3) Metaspace: 6347K->6347K(1056768K)
[0.223s][info][gc ] GC(3) Pause Young (G1 Humongous Allocation) 3M->3M(10M) 0.720ms
[0.223s][info][gc,cpu ] GC(3) User=0.00s Sys=0.00s Real=0.00s
[0.224s][info][gc,start ] GC(4) Pause Young (G1 Humongous Allocation)
[0.224s][info][gc,task ] GC(4) Using 8 workers of 8 for evacuation
[0.224s][info][gc,phases ] GC(4) Pre Evacuate Collection Set: 0.0ms
[0.224s][info][gc,phases ] GC(4) Evacuate Collection Set: 0.3ms
[0.224s][info][gc,phases ] GC(4) Post Evacuate Collection Set: 0.1ms
[0.224s][info][gc,phases ] GC(4) Other: 0.1ms
[0.224s][info][gc,heap ] GC(4) Eden regions: 1->0(1)
[0.224s][info][gc,heap ] GC(4) Survivor regions: 1->1(1)
[0.224s][info][gc,heap ] GC(4) Old regions: 2->2
[0.224s][info][gc,heap ] GC(4) Humongous regions: 5->3
[0.224s][info][gc,metaspace ] GC(4) Metaspace: 6347K->6347K(1056768K)
[0.224s][info][gc ] GC(4) Pause Young (G1 Humongous Allocation) 6M->4M(10M) 0.517ms
[0.224s][info][gc,cpu ] GC(4) User=0.00s Sys=0.00s Real=0.00s
[0.224s][info][gc,start ] GC(5) Pause Full (Allocation Failure)
[0.224s][info][gc,phases,start] GC(5) Phase 1: Mark live objects
[0.227s][info][gc,stringtable ] GC(5) Cleaned string and symbol table, strings: 3756 processed, 16 removed, symbols: 26432 processed, 0 removed
[0.227s][info][gc,phases ] GC(5) Phase 1: Mark live objects 2.161ms
[0.227s][info][gc,phases,start] GC(5) Phase 2: Compute new object addresses
[0.227s][info][gc,phases ] GC(5) Phase 2: Compute new object addresses 0.714ms
[0.227s][info][gc,phases,start] GC(5) Phase 3: Adjust pointers
[0.229s][info][gc,phases ] GC(5) Phase 3: Adjust pointers 1.276ms
[0.229s][info][gc,phases,start] GC(5) Phase 4: Move objects
[0.229s][info][gc,phases ] GC(5) Phase 4: Move objects 0.817ms
[0.230s][info][gc,task ] GC(5) Using 8 workers of 8 to rebuild remembered set
[0.231s][info][gc,heap ] GC(5) Eden regions: 0->0(1)
[0.231s][info][gc,heap ] GC(5) Survivor regions: 1->0(1)
[0.231s][info][gc,heap ] GC(5) Old regions: 2->2
[0.231s][info][gc,heap ] GC(5) Humongous regions: 3->3
[0.231s][info][gc,metaspace ] GC(5) Metaspace: 6347K->6347K(1056768K)
[0.231s][info][gc ] GC(5) Pause Full (Allocation Failure) 4M->4M(10M) 6.814ms
[0.231s][info][gc,cpu ] GC(5) User=0.02s Sys=0.00s Real=0.01s
[0.231s][info][gc,start ] GC(6) Pause Full (Allocation Failure)
[0.231s][info][gc,phases,start] GC(6) Phase 1: Mark live objects
[0.234s][info][gc,stringtable ] GC(6) Cleaned string and symbol table, strings: 3740 processed, 255 removed, symbols: 26432 processed, 0 removed
[0.234s][info][gc,phases ] GC(6) Phase 1: Mark live objects 2.516ms
[0.234s][info][gc,phases,start] GC(6) Phase 2: Compute new object addresses
[0.234s][info][gc,phases ] GC(6) Phase 2: Compute new object addresses 0.502ms
[0.234s][info][gc,phases,start] GC(6) Phase 3: Adjust pointers
[0.235s][info][gc,phases ] GC(6) Phase 3: Adjust pointers 1.200ms
[0.235s][info][gc,phases,start] GC(6) Phase 4: Move objects
[0.236s][info][gc,phases ] GC(6) Phase 4: Move objects 0.381ms
[0.236s][info][gc,task ] GC(6) Using 8 workers of 8 to rebuild remembered set
[0.237s][info][gc,heap ] GC(6) Eden regions: 0->0(1)
[0.237s][info][gc,heap ] GC(6) Survivor regions: 0->0(1)
[0.237s][info][gc,heap ] GC(6) Old regions: 2->2
[0.237s][info][gc,heap ] GC(6) Humongous regions: 3->3
[0.237s][info][gc,metaspace ] GC(6) Metaspace: 6347K->6301K(1056768K)
[0.237s][info][gc ] GC(6) Pause Full (Allocation Failure) 4M->4M(10M) 6.310ms
[0.237s][info][gc,cpu ] GC(6) User=0.00s Sys=0.00s Real=0.01s
[0.238s][info][gc,marking ] GC(2) Concurrent Mark From Roots 15.480ms
[0.238s][info][gc,marking ] GC(2) Concurrent Mark Abort
[0.238s][info][gc ] GC(2) Concurrent Cycle 15.763ms
[0.240s][info][gc,heap,exit ] Heap
[0.240s][info][gc,heap,exit ] garbage-first heap total 10240K, used 4286K [0x00000000ff600000, 0x00000000ff700050, 0x0000000100000000)
[0.240s][info][gc,heap,exit ] region size 1024K, 1 young (1024K), 0 survivors (0K)
[0.240s][info][gc,heap,exit ] Metaspace used 6345K, capacity 6380K, committed 6784K, reserved 1056768K
[0.240s][info][gc,heap,exit ] class space used 552K, capacity 570K, committed 640K, reserved 1048576K
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
at java.base/jdk.internal.misc.Unsafe.allocateUninitializedArray0(Unsafe.java:1257)
at java.base/jdk.internal.misc.Unsafe.allocateUninitializedArray(Unsafe.java:1250)
at java.base/java.lang.invoke.StringConcatFactory M e t h o d H a n d l e I n l i n e C o p y S t r a t e g y . n e w A r r a y ( S t r i n g C o n c a t F a c t o r y . j a v a : 1605 ) a t j a v a . b a s e / j a v a . l a n g . i n v o k e . D i r e c t M e t h o d H a n d l e MethodHandleInlineCopyStrategy.newArray(StringConcatFactory.java:1605) at java.base/java.lang.invoke.DirectMethodHandle MethodHandleInlineCopyStrategy.newArray(StringConcatFactory.java:1605)atjava.base/java.lang.invoke.DirectMethodHandleHolder.invokeStatic(DirectMethodHandle H o l d e r ) a t j a v a . b a s e / j a v a . l a n g . i n v o k e . L a m b d a F o r m Holder) at java.base/java.lang.invoke.LambdaForm Holder)atjava.base/java.lang.invoke.LambdaFormBMH/509886383.reinvoke(LambdaForm B M H ) a t j a v a . b a s e / j a v a . l a n g . i n v o k e . L a m b d a F o r m BMH) at java.base/java.lang.invoke.LambdaForm BMH)atjava.base/java.lang.invoke.LambdaFormBMH/1908316405.reinvoke(LambdaForm B M H ) a t j a v a . b a s e / j a v a . l a n g . i n v o k e . L a m b d a F o r m BMH) at java.base/java.lang.invoke.LambdaForm BMH)atjava.base/java.lang.invoke.LambdaFormMH/672320506.linkToTargetMethod(LambdaForm$MH)
at com.yyx.runtest.T2.main(T2.java:16)
Process finished with exit code 1

GC算法

GC算法:分带收集算法

次数上频繁收集Young区
次数上较少收集Old区
基本不动元空间

GC算法总体概述

在这里插入图片描述
java8以后,永久代变为元空间
JVM在进行GC时,并非每次都对上面三个区一起回收,大部分时候都回收新生代
因此,GC按照回收的区域又分了两种类型,一种是普通GC(minor GC),一种是全局GC(major GC or Full GC)

普通GC和全局GC的区别
  • 普通GC:只针对新生代区域内的GC,只在新生代发生垃圾收集动作,因为大多数Java对象的存活率都不高,所以普通GC非常频繁,一般回收速度也比较快
  • 全局GC:指发生在老年代的垃圾收集动作,出现一次全局GC,经常伴随至少一次的普通GC。全局GC的速度一般比普通GC慢10倍以上,(全空间扫描范围大)。
GC四大算法
引用计数算法

只需要了解
一个对象被应用一次,数字加一,当一个对象数字为零,则被GC
两大缺点

每次对对象赋值时均要维护引用计数器,而且计数器本身也有一定消耗
较难处理循环引用

故:JVM的实现一般不采用这种方式

复制算法

年轻代中使用的是普通GC,这种GC算法采用的是复制算法
原理:

Minor GC会把Eden中的所有活的对象都移到Survivor区域中,如果Survivor区中放不下,那么剩下的活的对象就被移到Old generation中,也即一旦收集后,Eden是就变成空的了。
当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,通过-XX:MaxTenuringThreshold 来设定参数),这些对象就会成为老年代。
-XX:MaxTenuringThreshold — 设置对象在新生代中存活的次数(Java8现在最多15)

解释:

年轻代中的GC,主要是复制算法(Copying)
HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1:1,一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。因为年轻代中的对象基本都是朝生夕死的(90%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片(缺点:耗空间)
在这里插入图片描述

总结:
复制算法优点:效率高,没有复制碎片
缺点:
1.浪费了一半的内存(from和to中在某时段有一个为空)
2.如果对象存活率很高,假设100%存活,我们就需要将所有对象都复制一遍,并将所有的引用地址重置一遍,复制这一工作所花费的时间,在对象存活率达到一定程度时,就变得不可忽视了。
以上描述不难看出,复制算法想使用,对象存活率一定要非常低,而且,必须克服50%的内存浪费

标记清除

老年代一般是有标记清除或者标记清除与标记整理混合实现
原理:
在这里插入图片描述
在这里插入图片描述
用通俗的话讲,就是当程序运行期间,若可以使用的内存被耗尽的时候,GC线程就会被触发并将程序暂停,随后将要回收的对象标记一遍,最终统一回收这些对象,完成标记清理工作接下来便让应用程序恢复运行。

总结:
缺点:效率低(扫描两次),会有内存碎片
优点:不需要额外的空间

标记压缩

老年代一般是由标记清除或者是标记清除与标记整理的混合实现
原理
在这里插入图片描述
在整理压缩阶段,不再对标记的对像做回收,而是通过所有存活对像都向一端移动,然后直接清除边界以外的内存。
可以看到,标记的存活对象将会被整理,按照内存地址依次排列,而未被标记的内存会被清理掉。如此一来,当我们需要给新对象分配内存时,JVM只需要持有一个内存的起始地址即可,这比维护一个空闲列表显然少了许多开销。
劣势:
标记/整理算法唯一的缺点就是效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。
从效率上来说,标记/整理算法要低于复制算法。
优化(工作中常用) 多次标记清除后压缩
在这里插入图片描述

哪种算法最好???

分代收集算法:没有最好的算法,只有最合适的
年轻代:复制算法
老年带:区域比较大,存活率高=》标记清除或者标记清除后标记整理

G1垃圾算法

所有加载,静态先行。(属于所有实例共用的)
静态的只被加载一次
JVM语法规定:
静态>构造块>构造方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值