JVM1
1.JVM组成(5个部分)
1.堆
new Object() 所有的对象都是存在此区域,此区域也是JVM中最大的一块区域
JVM垃圾回收就是针对此区域
2.JVM栈(Java虚拟机栈)
a.局部变量:8大基础数据类型,对象的引用
b.操作栈:每个方法都会对应一个操作栈
c.动态链接:指向常量池的方法引用
d.方法返回地址:PC寄存器的地址
3.本地方法栈
它与JVM栈比较类似,只不过JVM栈是给java和JVM使用,而本地方法栈是为本地方法(c/c++)服务
4.程序计数器
用来记录线程执行的行号
5.元空间(JDK 1.8) JDK 1.7 方法区(永久代)
运行时常量信息,字符串常量池,类的元信息(JDK 1.7 )
JDK 1.8 元空间:本地内存,并且将字符串移动到堆里面
堆划分:
新生代:第一次创建的对象都会分配到此区域
老年代:经历了一定的垃圾回收之后,依然存活下来的对象会移动到老年代;大对象在创建的时候也会直接进入老年代
HotSort默认的执行次数是15,经历15GC就会从新生代转移到老年代
新生代区域划分:
1.Eden:80%内存
2.S0:10%内存
3.S1:10%内存
新生代内存的利用率就可以达到90%
为什么大对象会直接进入老年代?
答:核心原因是因为大对象的初始化比较耗时,如果频繁的创建和消耗会带来一定的性能开销,因此最好的实现方式是将它存入GC频率更低的老年代
2.类加载机制(JVM加载)
加载(loading) -> 验证 -> 准备 -> 解析 -> 初始化 -> 使⽤ -> 卸载
1.加载
“加载”(Loading)阶段是整个“类加载”(Class Loading)过程中的⼀个阶段。在加载阶段,Java虚拟机需要完成以下三件事情:
1)通过⼀个类的全限定名来获取定义此类的⼆进制字节流。
2)将这个字节流所代表的静态存储结构转化为⽅法区的运⾏时数据结构。
3)在内存中⽣成⼀个代表这个类的java.lang.Class对象,作为⽅法区这个类的各种数据的访问⼊⼝。
2.验证
验证是连接阶段的第⼀步,这⼀阶段的⽬的是确保Class⽂件的字节 流中包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当作代码运⾏后不会危害虚拟机⾃身的安全
验证选项:
⽂件格式验证
字节码验证
符号引⽤验证…
3.准备
准备阶段是正式为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。
⽐如此时有这样⼀⾏代码:
public static int value = 123;
它是初始化 value 的 int 值为 0,⽽⾮ 123。
4.解析
解析阶段是 Java 虚拟机将常量池内的符号引⽤替换为直接引⽤的过程,也就是初始化常量的过程。
5.初始化
初始化阶段,Java 虚拟机真正开始执⾏类中编写的 Java 程序代码,将主导权移交给应⽤程序。初始化阶段就是执⾏类构造器⽅法的过程。
JVM双亲委派模型
当加载一个类的时候,那么这个类不会直接加载,而是将这个加载任务直接交给父类。当找不到父类的时候,才会自己尝试去加载
双亲委派模型优点:
1.唯一性:父类执行加载一次
2.安全性:会往上找而上层的类是系统提供的类,避免加载自定义的类,从而一定程度上保证了安全性
破坏双亲委派模型
1.JDK 1.2 提出的双亲委派模型,为了兼容老代码,因此在JDK1.2的时候已经出现了破坏双亲委派模型的场景
2.因为双亲委派模型自身的缺点而导致的,比如在父类当中要调用子类的方法是没办法实现。
3.人们对于热更新的需求,导致了双亲委派模型的有一次破坏
3.垃圾回收
1.判别死亡对象(垃圾)
a.引用计数器算法
给每个对象创建一个计数器,当有程序引用此类的时候计数器+1,当计数器为0时,表示此对象没人用,那么就可以将它归为死亡对象,等待垃圾回收器的回收.
缺点:循环引用的问题
b.可达性分析算法
此算法的核心思想为 : 通过一系列称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径称之为"引用链",当一个对象到GC Roots没有任何的引用链相连时(从GC Roots到这个对象不可达)时,证明此对象是不可用的。(HotSpot默认使用的算法)
2.垃圾回收算法
标记清除算法(老年代)
1.标记(可达性分析 存活的对象和死亡对象)
2.清除
缺点:内存碎片
复制算法(新生代)
"复制"算法是为了解决"标记-清理"的效率问题。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块内存需要进行垃圾回收时,会将此区域还存活着的对象复制到另一块上面,然后
再把已经使用过的内存区域一次清理掉。这样做的好处是每次都是对整个半区进行内存回收,内存分配时也就不需要考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配即可。
标记-整理算法(老年代回收算法)
复制收集算法在对象存活率较高时会进行比较多的复制操作,效率会变低。因此在老年代一般不能使用复制算法。
针对老年代的特点,提出了一种称之为"标记-整理算法"。标记过程仍与"标记-清除"过程一致,但后续步骤不是直接对可回收对象进行清理,而是让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存。
3.垃圾回收器
1.Serial收集器(新生代收集器,串行GC)
Serial收集器是最基本、发展历史最悠久的收集器,曾经(在JDK 1.3.1之前)是虚拟机新生代收集的唯一选择。
特性:
这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束(Stop The World).
应用场景:
Serial收集器是虚拟机运行在Client模式下的默认新生代收集器。
优势:
简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,Serial收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。 实际上到现在为止 : 它依然是虚拟机运行在Client模式下的默认新生代收集器
2.Serial Old收集器(老年代收集器,串行GC)(标记-整理算法)
3.ParNew收集器(新生代收集器,并行GC)
4.Parallel Scavenge收集器(新生代收集器,并行GC)
5.Parallel Old收集器(老年代收集器,并行GC)
6.CMS收集器(老年代收集器,并发GC)
7.G1收集器(唯一一款全区域的垃圾回收器)
JDK11(默认的垃圾回收器)
一个region有可能属于Eden,Survivor或者Tenured内存区域。图中的E表示该region属于Eden内存区域,S表示属于Survivor内存区域,T表示属于Tenured内存区域。图中空白的表示未使用的内存空间。G1垃圾收集器还增加了一种新的内存区域,叫做Humongous内存区域,如图中的H块。这种内存区域主要用于存储大对象-即大小超过一个region大小的50%的对象。