JVM--藤原豆腐店自用
目录
1.类加载
1.1.JVM类加载过程:
1.2.类的初始化过程
1.3.类加载器
1.3.1.启动类加载器(BootStrap ClassLoader)
1.3.2.扩展类加载器(Extension ClassLoader)
1.3.3.系统类加载器(App ClassLoader)
1.3.4.java.lang.ClassLoader 类
1.3.5.类加载器的代理模式--双亲委托机制
2.JVM内存管理
2.1.Java代码是如何运行的
2.2.JVM运行原理图
3.3.JVM内存结构划分
2.4.JVM内存相关的核心参数
2.5.JVM运行时数据区 程序计数器 的特点及作用?
2.6.JVM运行时数据区 虚拟机栈的特点及作用?
2.7.JVM运行时数据区 本地方法栈的特点及作用?
2.8.JVM运行时数据区 Java堆的特点及作用?
2.9.JVM运行时数据区 元空间的特点及作用
2.10.JVM本机直接内存的特点及作用
3.垃圾回收
3.1.JVM如何判断对象可以被回收?
3.1.1哪些对象可以作为GC Roots呢?
3.1.2JVM堆内存分代模型?
3.2.JVM堆中新生代的垃圾回收过程
3.3.JVM对象动态年龄判断
3.4.老年代空间分配担保机制
3.5.什么情况下对象会进入老年代?
3.6.堆为什么要分成年轻代和老年代?
3.7JVM中的垃圾回收算法
3.7.1.标记-清除算法
3.7.2.复制算法
3.7.3.标记-整理算法
3.7.4.分代收集算法
3.8.JVM堆的年轻代为什么要有两个Survivor区
3.8.1.如果没有Survivor区会怎么样?
3.8.2.如果只有1个Survivor区会怎么样?
3.9.Eden区与Survivor区的空间大小比值为什么默认是8:1:1?
3.10.Minor GC、Major GC和Full GC
3.10.1.Minor GC
3.10.2.Major GC
3.10.3.Full GC
1.类加载
1.1.JVM类加载过程:
- 加载(Loading)
- 验证(Verification)
- 准备(Preparation)
- 解析(Resolution)
- 初始化(Initialization)
- 使用(Using)
- 卸载(Unloading)
验证、准备、解析三个阶段统称为链接(Linking)
类加载生命周期:
1.加载:
classpass、jar包、网络、某个磁盘位置下的类的class二进制字节流读进来,在内存中生成一个代表这个类的java.lang.Class对象放入元空间,此阶段我们程序员可以干预,我们可以自定义类加载器来实现类的加载;
2.验证:
验证Class文件的字节流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证虚拟机的安全;
3.准备:
类变量赋默认初始值,int为0,long为0L,boolean为false,引用类型为null;常量赋正式值;
4.解析:
把符号引用翻译为直接引用;
5.初始化:
当我们new一个类的对象,访问一个类的静态属性,修改一个类的静态属性,调用一个类的静态方法,用反射API对一个类进行调用,初始化当前类,其父类也会被初始化...... 那么这些都会触发类的初始化;
6.使用:
使用这个类;
7.卸载:
1.)该类所有的实例都已经被GC,也就是JVM中不存在该Class的任何实例;
2.)加载该类的ClassLoader已经被GC;
3.)该类的java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法;
1.2.类的初始化过程
类的初始化阶段,Java虚拟机才真正开始执行类中编写的Java程序代码;
进行准备阶段时,变量已经赋过一次系统要求的初始零值,而在初始化阶段,才真正初始化类变量和其他资源;
继承时父子类的初始化顺序是怎样的?
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
父类--变量
父类--初始化块
父类--构造器
子类--变量
子类--初始化块
子类--构造器
1.3.类加载器
1.3.1.启动类加载器(BootStrap ClassLoader)
是java类加载层次中最顶层的类加载器,负责加载jdk中的核心类库。由C++实现,不是classLoader的子类
1.3.2.扩展类加载器(Extension ClassLoader)
负责加载java的扩展类库,比如lib/ext或者java.ext.dirs系统属性指定的目录中的jar包。父类加载器为null。
1.3.3.系统类加载器(App ClassLoader)
负责加载来自java命令的-classpath选项、java.class.path系统属性所指定的jar包和类路径。程序可以通过classLoader的静态方法getSystemClassLoader(),来获取系统类加载器。由java语言实现,父类加载器为ExtClassLoader。
1.3.4.java.lang.ClassLoader 类
java.lang.ClassLoader 类的基本职责就是根据一个指定的类的名称,找到或者生成其对应的字节代码,然后从这些字节代码中定义出一个 Java 类,即java.lang.Class 类的一个实例。除此之外,ClassLoader 还负责加载 Java 应用所需的资源,如图像文件和配置文件等。
1.3.5.类加载器的代理模式--双亲委托机制
就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归 (本质上就是loadClass函数的递归调用)。因此,所有的加载请求最终都应该传送到顶层的启动类加载器中。如果父类加载器可以完成这个类加载请求,就成功返回;只有当父类加载器无法完成此加载请求时,子加载器才会尝试自己去加载。
- 双亲委托机制是为了保证 Java 核心库的类型安全。这种机制就保证不会出现用户自己定义的 java.lang.Object 类的情况。
- tomcat 服务器类加载器也使用代理模式,所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类。这与一般类加载器的顺序是相反的。
- 并不是所有的类加载器都是双亲委托机制。
2.JVM内存管理
2.1.Java代码是如何运行的
.java文件通过javac编译成.class文件。每一个java程序都是通过xxx\jdk1.8.0_251\bin\java启动一个JVM虚拟机,在虚拟机里面运行Mall.class字节码文件。
2.2.JVM运行原理图
3.3.JVM内存结构划分
加载进来的.class字节码文件、代码执行创建的对象、代码执行调用方法,方法中有变量等数据需要一个地方存放,所以JVM划分出了几个区域,用于存放这些信息;
hotspot
JDK1.8之前,元空间就是原来的方法区(永久代);
注意:
1. 堆、元空间是线程共享的;
2.其他区是线程私有的;
2.4.JVM内存相关的核心参数
-Xms Java堆内存的大小;
-Xmx Java堆内存的最大大小;
-Xmn Java堆内存中的新生代大小,扣除新生代剩下的就是老年代的内存大小;
-XX:MetaspaceSize 元空间大小;
-XX:MaxMetaspaceSize 元空间最大大小;
-Xss 每个线程的栈内存大小;
-XX:SurvivorRatio=8 设置eden区 和survivor 区大小的比例,默认是8:1:1;
-XX:MaxTenuringThreshold=5 年龄阈值;
-XX:+UseConcMarkSweepGC 指定CMS垃圾收集器;
-XX:+UseG1GC 指定使用G1垃圾回收器
--查看默认的堆大小及默认的垃圾收集器
java -XX:+PrintCommandLineFlags -version
2.5.JVM运行时数据区 程序计数器 的特点及作用?
1、程序计数器是一块较小的内存空间,几乎可以忽略;
2、是当前线程所执行的字节码的行号指示器;
3、Java多线程执行时,每条线程都有一个独立的程序计数器,各条线程之间计数器互不影响;
4、该区域是“线程私有”的内存,每个线程独立存储;
5、该区域不存在OutOfMemoryError;
6、无GC回收;
2.6.JVM运行时数据区 虚拟机栈的特点及作用?
1、线程私有;
2、方法执行会创建栈帧,存储局部变量表等信息;
3、方法执行入虚拟机栈,方法执行完出虚拟机栈;(先进后出)
4、栈深度大于虚拟机所允许StackOverflowError;
5、栈需扩展而无法申请空间OutOfMemoryError(比较少见);hotspot虚拟机没有;
6、栈里面运行方法,存放方法的局部变量名,变量名所指向的值(常量值、对象值等)都存放到堆上的;
7、栈一般都不设置大小,栈所占的空间其实很小,可以通过-Xss1M进行设置,如果不设置默认为1M;
8、随线程而生,随线程而灭;
9、该区域不会有GC回收;
2.7.JVM运行时数据区 本地方法栈的特点及作用?
1、与虚拟机栈基本类似;
2、区别在于本地方法栈为Native方法服务;
3、HotSpot虚拟机将虚拟机栈和本地方法栈合并;
4、有StackOverflowError和OutOfMemoryError(比较少见);
5、随线程而生,随线程而灭;
6、GC不会回收该区域;
程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭;
2.8.JVM运行时数据区 Java堆的特点及作用?
1、线程共享的一块区域;
2、虚拟机启动时创建;
3、虚拟机所管理的内存中最大的一块区域;
4、存放所有实例对象或数组;
5、GC垃圾收集器的主要管理区域;
6、可分为新生代、老年代;
7、新生代更细化可分为Eden、From Survivor、To Survivor,Eden:Survivor = 8:1:1
8、可通过-Xmx、-Xms调节堆大小;
9、无法再扩展java.lang.OutOfMemoryError: Java heap space
10、如果从分配内存的角度看,所有线程共享的Java堆中可以划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB),以提升对象分配时的效率;
2.9.JVM运行时数据区 元空间的特点及作用
1、在JDK1.8开始才出现元空间的概念,之前叫方法区/永久代;
2、元空间与Java堆类似,是线程共享的内存区域;
3、存储被加载的类信息、常量、静态变量、常量池、即时编译后的代码等数据;
4、元空间采用的是本地内存,本地内存有多少剩余空间,它就能扩展到多大空间,也可以设置元空间大小;
-XX:MetaspaceSize=20M -XX:MaxMetaspaceSize=20m
5、元空间很少有GC垃圾收集,一般该区域回收条件苛刻,能回收的信息比较少,所以GC很少来回收;
6、元空间内存不足时,将抛出OutOfMemoryError;
2.10.JVM本机直接内存的特点及作用
1、直接内存(Direct Memory)不属于JVM运行时数据区,是本机直接物理内存;
2、像在JDK 1.4中新加入了NIO(New Input/Output)类,一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的 DirectByteBuffer对象作为这块内存的引用进行操作,这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据;
3、可能导致OutOfMemoryError异常出现; netty
3.垃圾回收
Garbage Collection(GC)
3.1.JVM如何判断对象可以被回收?
在JVM堆里面存放着所有的Java对象,垃圾收集器在对堆进行回收前,首先要确定这些对象之中哪些还“存活”着,哪些已经“死去”;
Java通过 可达性分析(Reachability Analysis)算法 来判定对象是否存活的;
该算法的基本思路:通过一系列称为“GC Roots”的根对象作为起始节点,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连(也称为不可达),则证明此对象是不可能再被使用的对象,就可以被垃圾回收器回收;
对象object 5、object 6、object 7虽然有关联,但它们到GC Roots是不可达的,所以它们将会被判定为可回收的对象;
3.1.1哪些对象可以作为GC Roots呢?
1、在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等所引用的对象;
2、方法区/元空间中的类静态属性引用的对象;
3、方法区/元空间中的常量引用的对象;
4、在本地方法栈中JNI(即通常所说的Native方法)引用的对象;
5、Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如
NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器;
6、所有被同步锁(synchronized关键字)持有的对象;
7、反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等;
8、其他可能临时性加入的对象;
3.1.2JVM堆内存分代模型?
JVM堆内存的分代模型:年轻代、老年代;
大部分对象朝生夕死,少数对象长期存活;
3.2.JVM堆中新生代的垃圾回收过程
JVM里垃圾回收针对的是 新生代,老年代,还有元空间/方法区(永久代),
不会针对方法的栈帧进行回收,方法一旦执行完毕,栈帧出栈,里面的局部变量直接就从内存里清理掉,也就是虚拟机栈不存在垃圾回收;
代码里创建出来的对象,一般就是两种:
1、一种是短期存活的,分配在Java堆内存之后,迅速使用完就会被垃圾回收;
2、一种是长期存活的,需要一直生存在Java堆内存里,让程序后续不停的去使用;
第一种短期存活的对象,是在Java堆内存的新生代里分配;
第二种长期存活的对象,通过在新生代S0区和S1区来回被垃圾回收15次后,进入Java堆内存的老年代中,这里的15次,我们也称为对象的年龄,即对象的年龄为15岁;
java -XX:+PrintFlagsFinal 打印jvm默认参数值;
3.3.JVM对象动态年龄判断
虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold=15才能晋升老年代;
结论-->动态年龄判断:Survivor区的对象年龄从小到大进行累加,当累加到X年龄(某个年龄)时占用空间的总和大于50%(可以使用-XX:TargetSurvivorRatio=? 来设置保留多少空闲空间,默认值是50),那么比X年龄大的对象都会晋升到老年代;
1、Survivor区分布如下图:
1-3岁总和小于50%
2、此时新生代GC后,有6%的对象进入Survivor区,Survivor区分布如下图:
这时从1岁加到4岁时,总和51% 大于50%,但此时没有大于四岁的对象,即没有对象晋升
3、又经过一次新生代GC后,有40%的对象进入Survivor区,Survivor区分布如下图:
Survivor区的对象年龄从小到大进行累加,当累加到 3 年龄时的总和大于50%,那么比3大的都会晋升到老年代,即4岁的20%、5岁的20%晋升到老年代;
3.4.老年代空间分配担保机制
新生代Minor GC后剩余存活对象太多,无法放入Survivor区中,此时就必须将这些存活对象直接转移到老年代去,如果此时老年代空间也不够怎么办?
1、执行任何一次Minor GC之前,JVM会先检查一下老年代可用内存空间,是否大于新生代所有对象的总大小,因为在极端情况下,可能新生代Minor GC之后,新生代所有对象都需要存活,那就会造成新生代所有对象全部要进入老年代;
2、如果老年代的可用内存大于新生代所有对象总大小,此时就可以放心大胆的对新生代发起一次Minor GC,因为Minor GC之后即使所有对象都存活,Survivor区放不下了,也可以转移到老年代去;
3、如果执行Minor GC之前,检测发现老年代的可用空间已经小于新生代的全部对象总大小,那么就会进行下一个判断,判断老年代的可用空间大小,是否大于之前每一次Minor GC后进入老年代的对象的平均大小,如果判断发现老年代的内存大小,大于之前每一次Minor GC后进入老年代的对象的平均大小,那么就是说可以冒险尝试一下Minor GC,但是此时真的可能有风险,那就是Minor GC过后,剩余的存活对象的大小,大于Survivor空间的大小,也大于老年代可用空间的大小,老年代都放不下这些存活对象了,此时就会触发一次“Full GC”;
所以老年代空间分配担保机制的目的?也是为了避免频繁进行Full GC;
- 如果Full GC之后,老年代还是没有足够的空间存放Minor GC过后的剩余存活对象,那么此时就会导致“OOM”内存溢出 ;
在JDK6的时候有一个参数-XX:+HandlePromotionFailure用于开启是否要进行空间担保;
3.5.什么情况下对象会进入老年代?
1、躲过15次GC之后进入老年代,可通过JVM参数“-XX:MaxTenuringThreshold”来设置年龄,默认为15岁;
2、动态对象年龄判断;
3、老年代空间担保机制;
4、大对象直接进入老年代;
大对象是指需要大量连续内存空间的Java对象,比如很长的字符串或者是很大的数组或者List集合,大对象在分配空间时,容易导致内存明明还有不少空间时就提前触发垃圾回收以获得足够的连续空间来存放它们,而当复制对象时,大对象又会引起高额的内存复制开销,为了避免新生代里出现那些大对象,然后屡次躲过GC而进行来回复制,此时JVM就直接把该大对象放入老年代,而不会经过新生代;
我们可以通过JVM参数“-XX:PretenureSizeThreshold”设置多大的对象直接进入老年代,该值为字节数,比如“1048576”字节就是1MB,该参数表示如果创建一个大于这个大小的对象,比如一个超大的数组或者List集合,此时就直接把该大对象放入老年代,而不会经过新生代;
-XX:PretenureSizeThreshold参数只对Serial和ParNew两款新生代收集器有效,其他新生代垃圾收集器不支持该参数,如果必须使用此参数进行调优,可考虑 ParNew+CMS的收集器组合;
3.6.堆为什么要分成年轻代和老年代?
因为年轻代和老年代不同的特点,需要采用不同的垃圾回收算法;
年轻代的对象,它的特点是创建之后很快就会被回收,所以需要用一种垃圾回收算法;
老年代的对象,它的特点是需要长期存活,所以需要另外一种垃圾回收算法 ;
所以需要分成两个区域来放不同的对象;
1、绝大多数对象都是朝生夕灭的;
如果一个区域中大多数对象都是朝生夕灭,那么把它们集中放在一起,每次回收时只关注如何保留少量存活对象,而不是去标记那些大量将要被回收的对象,就能以较低的代价回收到大量的空间;
2、熬过越多次垃圾收集的对象就越难以回收;
如果是需要长期存活的对象,那把它们集中放在一块,虚拟机便可以使用较低的频率来回收这个区域,这就同时兼顾了垃圾收集的时间开销和内存的空间有效利用;
3、JVM划分出新生代、老年代之后,垃圾收集器可以每次只回收其中某一个或者某些部分的区域 ,同时也有了“Minor GC”“Major GC”“Full GC”这样的回收类型的划分;
Minor GC/Young GC :新生代收集
Major GC/Old GC:老年代收集
Full GC:整堆收集,收集整个Java堆和元空间/方法区的垃圾收集;
Mixed GC:混合收集,收集整个新生代以及部分老年代的垃圾收集,目前只有G1收集器会有这种行为;
4、针对不同的区域对象存亡特征采用不同的垃圾收集算法:
(1)复制算法
(2)标记-清除算法
(3)标记-整理算法
3.7JVM中的垃圾回收算法
3.7.1.标记-清除算法
标记-清除算法是最基础的收集算法,后续的很多垃圾回收算法是基于该算法而发展出来的,它分为‘ 标记 ’和‘ 清除 ’两个阶段;
1、标记
标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记出所有存活的对象,在标记完成后,统一回收所有未被标记的对象,标记过程就是对象是否属于垃圾的判定过程,基于可达性分析算法判断对象是否可以回收;
2、清除
标记后,对所有被标记的对象进行回收;
优点:基于最基础的可达性分析算法,实现简单,后续的收集算法都是基于这种思想实现的;
缺点:
- 执行效率不稳定,如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低;
2、内存空间的碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集;
3.7.2.复制算法
复制算法是标记-复制算法的简称,将可用内存按容量分为大小相等的两块,每次只使用其中一块,当这一块的内存用完了,就将还存活的对象复制到另外一块内存上,然后再把已使用过的内存空间一次清理掉;
优点:实现简单,效率高,解决了标记-清除算法导致的内存碎片问题;
缺点:
- 代价太大,将可分配内存缩小了一半,空间浪费太多了;
- 对象存活率较高时就要进行较多的复制操作,效率将会降低;
一般虚拟机都会采用该算法来回收新生代,但是JVM对复制算法进行了改进,JVM并没有按照1:1的比例来划分新生代的内存空间,因为通过大量的统计和研究表明,90%以上的对象都是朝生夕死的,所以JVM把新生代分为一块较大的Eden空间和两块较小的Survivor空间,每次分配内存只使用Eden和其中一块Survivor,发生垃圾收集时,将Eden和Survivor中仍然存活的对象一次性复制到另外一块Survivor空间上,然后直接清理掉Eden和已用过的那块Survivor空间,HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也即每次新生代中可用内存空间为整个新生代容量的90%(Eden的80%加上一个Survivor的10%),只有另外一个Survivor空间即10%的新生代会被“浪费”;
当然,90%的对象可被回收仅仅是大部分情况下,我们无法百分百保证每次回收都只有不多于10%的对象存活,因此JVM还有一个空间担保机制的安全设计,当Survivor空间不足以容纳一次Minor GC之后存活的对象时,就需要依赖其它内存区域(实际上就是老年代)进行空间分配担保(Handle Promotion,也就是冒险Minor GC一下);
3.7.3.标记-整理算法
标记-整理算法是根据老年代的特点而产生的;
1、标记
标记过程与上面的标记-清理算法一致,也是基于可达性分析算法进行标记;
2、整理
和标记-清理不同的是,该算法不是针对可回收对象进行清理,而是根据存活对象进行整理,让存活对象都向一端移动,然后直接清理掉边界以外的内存;
而标记-清除算法不移动存活对象,导致有大量不连续空间,即内存碎片,而老年代这种每次回收都有大量存活对象的区域,移动存活对象并更新所有引用这些对象的引用,这是一种比较耗时的操作,而且这种对象移动操作必须全程暂停用户应用程序才能进行,像这样的停顿我们也称为“Stop The World”即STW;
但是即便是移动存活对象是耗时的操作,但是如果不这么做,那么在充满内存碎片的空间中分配对象,又影响了对象的分配和访问的效率,所以JVM权衡两者之后,还是采用了移动存活对象的方式,也就是对内存进行了整理;
另外像cms垃圾收集器,平时多数时间都采用标记-清除算法,暂时容忍内存碎片的存在,直到内存空间的碎片化程度已经大到影响对象分配时,再采用标记-整理算法收集一次,以获得规整的内存空间,所以像基于标记-清除算法的CMS收集器面临空间碎片过多时就会进行一次整理;
优点:
- 不会像复制算法那样划分两个区域,提高了空间利用率;
- 不会产生不连续的内存碎片;
缺点:效率问题,除了像标记-清除算法的标记过程外,还多了一步整理过程,效率变低;
3.7.4.分代收集算法
现在一般虚拟机的垃圾收集都是采用“ 分代收集 ”算法;
根据对象存活周期的不同将内存划分为几块,一般把java堆分为新生代和老年代,JVM根据各个年代的特点采用不同的收集算法;
新生代中,每次进行垃圾回收都会发现大量对象死去,只有少量存活,因此采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;
老年代中,因为对象存活率较高,采用标记-清理、标记-整理算法来进行回收;
3.8.JVM堆的年轻代为什么要有两个Survivor区
3.8.1.如果没有Survivor区会怎么样?
此时每触发一次Minor GC,就会把Eden区的对象复制到老年代,这样当老年代满了之后会触发Major Gc/Full GC(通常伴随着MinorGC),比较耗时,所以必须有Survivor区;
3.8.2.如果只有1个Survivor区会怎么样?
刚刚创建的对象在Eden中,一旦Eden满了,触发一次Minor GC,Eden中存活的对象就会被移动到Survivor区,下一次Eden满了的时候,此时进行Minor GC,Eden和Survivor各有一些存活对象,因为只有一个Survivor,所以Eden区第二次GC发现的存活对象也是放入唯一的一个Survivor区域中,但此时把Eden区的存活对象硬放到Survivor区,很明显这两部分对象所占有的内存是不连续的,也就导致了内存碎片化问题,并且由于不连续的空间会导致再分配大对象的时候,由于没有连续的空间来分配,会导致提前垃圾回收;
如果将Survivor中的所有存活对象进行整理消除碎片,然后将所有的存活对象放入其中,这样做会降低效率;
如果把两个区域中的所有存活对象都复制转移到一个完全独立的空间中,也就是第二块Survivor中,这样就可以留出一块完全空着的Eden和Survivor了,下次GC的时候再重复这个流程,所以我们便要有两个Survivor区;
3.9.Eden区与Survivor区的空间大小比值为什么默认是8:1:1?
一个eden区 ,新生代对象出生的地方;
两个survivor区,一个用来保存上次新生代GC存活下来的对象,还有一个空着,在新生代GC时把eden+survivor中存活对象复制到这个空的survivor中;
统计和经验表明,90%的对象朝生夕死存活时间极短,每次gc会有90%对象被回收,剩下的10%要预留一个survivor空间去保存;
3.10.Minor GC、Major GC和Full GC
3.10.1.Minor GC
当年轻代(Eden区)满时就会触发 Minor GC,这里的年轻代满指的是 Eden区满。Survivor 满不会触发 Minor GC 。
内存池被填满的时候,其中的内容全部会被复制,指针会从0开始跟踪空闲内存。Eden 和 Survivor 区进行了标记和复制操作,取代了经典的标记、扫描、压缩、清理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是停留在所使用内存池的顶部。
执行 Minor GC 操作时,不会影响到永久代。从永久代到年轻代的引用被当成 GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉。
对于大部分应用程序,Minor GC 操作时应用程序停顿导致的延迟都是可以忽略不计的。因为,大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。如果正好相反,Eden 区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多
3.10.2.Major GC
CMS收集器中,当老年代满时会触发 Major GC。目前,只有CMS收集器会有单独收集老年代的行为。其他收集器均无此行为。
通常能单独发生收集行为的只是新生代的Minor GC,所以这里“反过来”的情况只是理论上允许,实际上除了CMS收集器,其他都不存在只针对老年代的收集。
3.10.3.Full GC
Full GC 对收集整堆(新生代、老年代)和方法区的垃圾收集。
当年老代满时会引发Full GC,Full GC将会同时回收新生代、年老代 ;
当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载 。
调用System.gc时,系统建议执行Full GC,但是不一定会执行。
JVM--藤原豆腐店自用相关推荐
- Java八股文--藤原豆腐店自用
目录 1.Java跨平台的原理 2.基本数据类型所占字节数 3.Java中表示价格为啥不用double,而是用BigDecimal 4.双精度&单精度的区别 5.面向对象的特征 5.1.封装: ...
- 线程八股文--藤原豆腐店自用
目录 1.什么是进程.什么是线程,他们之间是什么关系? 1.1.进程是什么? 1.2.线程是什么? 1.什么是进程.什么是线程,他们之间是什么关系? 1.1.进程是什么? 是具有一定独立功能的程序,他 ...
- Java反射--藤原豆腐店自用
什么是反射? JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为j ...
- 数据库八股文--藤原豆腐店自用
目录 数据库的分类和常见的数据库你知道那些? 关系型数据库 非关系型数据库 介绍一下三范式,设计表时一定要追求三范式吗? 数据库的分类和常见的数据库你知道那些? 关系型数据库 典型产品:MySQL.S ...
- JDBC--藤原豆腐店自用
Java Database Connectivity,简称JDBC 是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法. 驱动程序 驱动程序:数据 ...
- 龙族幻想最新东京机器人位置_龙族幻想藤原智坐标位置一览 藤原智任务攻略...
龙族幻想v1.3.148安卓版 类型:角色扮演大小:1.39G语言:中文 评分:5.0 标签: 立即下载 龙族幻想藤原智在哪?这是苍穹之下异闻,需要找到npc藤原智,许多玩家都不清楚在什么位置,如何完 ...
- The annotation of C++ primer {藤原豆腐坊自家用}
The annotation of <<C++ primer>> {藤原豆腐坊自家用} 给变量名一个初始值几乎总是正确的. 但不要求必须这么做 C++的主要设计目的之一就是允许 ...
- [画风突变] 藤原千花专属相册,C语言制作书记专属电子相册!
前言 一款自己制作的电子相册不仅富有创意,而且能够最大可能的满足己需,今天小编带着大家制作的就是藤原千花的电子相册.要是你愿意,你也可以把你和你对象的点滴放进去哦,弄点爱情宣言什么的都是没什么问题. ...
- JVM学习笔记(自用)
JVM学习笔记(自用) 文章目录 JVM学习笔记(自用) 1.简介 2.程序计数器 3. 虚拟机栈 4. 方法区 5. 直接内存 6. 垃圾回收 Young Collection Young Coll ...
最新文章
- Ubuntu定时任务crontab命令介绍
- 《深度学习,统计学习,数学基础》人工智能算法工程师手册:程序员写的AI书,50 章一网打尽...
- 拼多多“砍价免费拿”始终差“0.09%”遭起诉,官方回应:活动真实
- ubuntu下安装wine
- hdu 5037 Frog 贪心 dp
- 【转】Web实现音频、视频通信
- java constructors_[译]understanding constructors
- 2022-2027年(新版)中国石墨烯行业竞争态势及发展前景预测报告
- coherence mysql_Coherence Step by Step 第三篇 缓存(二) 配置(翻译)
- HCIE-Routing Switching V3.0 资料分享
- Prophet文档中文翻译--outliers
- 阿里云ECS服务器配置怎么选?
- gis可达性分析步骤_基于三维GIS技术的公路交通数字孪生系统
- 做伦敦银,这两大要点容易被忽视
- puppy linux 默认密码,puppy linux真的让我感觉眼前一亮!
- 当初我要是这么学习二叉树就好了「附图文解析」
- 超低延时直播rtmp推流
- 2018年异步畅销榜20本好书,本本都想要!
- 苹果微信html页面缓存,Ios中微信页面返回上一页去除缓存几种常见思路
- 淘宝当天旺旺提取软件操作教程
热门文章
- cannot have an existing value
- java.commen包的一些用法StringUtils.equals()
- 美国政府发布《软件供应链安全客户实践建议指南》
- 云南计算机一级c类基础知识,云南省大学计算机一级C类多选题及答案.pdf
- 电脑脑运行快捷键是什么,电脑运行窗口快捷键
- 多项式嵌套乘法及matlab实现
- 抖音logo制作教程
- 决策树CART、ID3、C4.5原理梳理
- mysql数据库特别大怎么备份_如何备份还原mysql数据库 mysql数据库太大备份与还原方法...
- 自动清理内存linux,linux 过滤内存使用率并于计划任务结合来自动清理内存缓存...