延后2

JVM知识汇总

2022-02-24  本文已影响0人  三十五岁养老

本文是学习之后的总结。一是为了梳理知识点,归纳总结,二是为了分享交流,如有错误之处还望指出。
[参考文献]
1、https://blog.csdn.net/qq_41701956/article/details/81664921
2、https://blog.csdn.net/qq_41701956/article/details/80020103
3、https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247484038&idx=1&sn=e083cc8b248461c8916a819119b059c3&chksm=ebf6daf9dc8153ef27ecd857e6cc85372735e84042679c133892d0993074371a46dd2c28b8b3&scene=21#wechat_redirect

JVM(Java Virtual Machine,Java虚拟机)

Java程序的跨平台特性主要是指字节码文件可以在任何具有Java虚拟机的计算机或者电子设备上运行,Java虚拟机中的Java解释器负责将字节码文件解释成为特定的机器码进行运行。因此在运行时,Java源程序需要通过编译器编译成为.class文件。

JVM 内存

[参考资料] (https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247483949&idx=1&sn=8b69d833bbc805e63d5b2fa7c73655f5&chksm=ebf6da52dc815344add64af6fb78fee439c8c27b539b3c0e87d8f6861c8422144d516ae0a837&scene=21#wechat_redirect
)
Java虚拟机内存结构分:JVM堆、方法区、虚拟机栈、本地方法栈、程序计数器

JVM 内存模型

内存区域中的程序计数器、虚拟机栈、本地方法栈这3个区域随着线程而生,线程而灭;栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈的操作,每个栈帧中分配多少内存基本是在类结构确定下来时就已知的

Java对象在内存中的状态

JVM 类加载

类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的 Class对象, Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。

加载.class的方式

类加载的过程包括了加载、验证、准备、解析、初始化五个阶段

加载

查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象

开发人员既可以使用系统提供的类加载器来完成加载,也可以自定义自己的类加载器来完成加载。

验证

验证:确保被加载的类的正确性(文件格式、元数据、字节码、符号引用验证)

验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用 -Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

准备

为类的 静态变量分配内存,并将其初始化为默认值

解析

解析:把类中的符号引用转换为直接引用
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。

初始化

为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。
在Java中对类变量进行初始值设定有两种方式:
①声明类变量是指定初始值
②使用静态代码块为类变量指定初始值
JVM初始化步骤

类初始化时机:只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:

结束生命周期
在如下几种情况下,Java虚拟机将结束生命周期

类加载器

类加载器

类加载器可以大致划分为以下三类:

类加载机制

双亲委托模式的好处

JVM GC

[参考资料]https://mp.weixin.qq.com/s?__biz=MzI4NDY5Mjc1Mg==&mid=2247483952&idx=1&sn=ea12792a9b7c67baddfaf425d8272d33&chksm=ebf6da4fdc815359869107a4acd15538b3596ba006b4005b216688b69372650dbd18c0184643&scene=21#wechat_redirect
jvm 中,程序计数器、虚拟机栈、本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的.

对象存活判断

标记-清除算法、复制算法、标记-整理算法。这三种算法都扩充了根搜索算法,

GC算法

GC最基础的算法有三种:标记 -清除算法、复制算法、标记-压缩算法,常用的垃圾回收器一般都采用分代收集算法。

标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象;
清除阶段:清除所有未被标记的对象。
缺点:
标记和清除的过程效率不高(标记和清除都需要从头遍历到尾)
标记清除后会产生大量不连续的碎片,当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象。
优点:这样使得每次都是对整个半区进行回收,内存分配时也就不用考虑内存碎片等情况
只要移动堆顶指针,按顺序分配内存即可,实现简单,运行效率高
缺点:空间的浪费复制算法要想使用,最起码对象的存活率要非常低才行

标记阶段:先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象
整理阶段:将将所有的存活对象压缩到内存的一端;之后,清理边界外所有的空间
优点:不会产生内存碎片。可以弥补标记-清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。
缺点:在标记的基础之上还需要进行对象的移动,成本相对较高,效率也不高。

GC分代的基本假设:绝大部分对象的生命周期都非常短暂,存活时间短。
“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。

CMS收集器更加关注停顿,它在做GC的时候是和用户线程一起工作的(并发执行),如果使用标记整理算法的话,那么在清理的时候就会去移动可用对象的内存空间,那么应用程序的线程就很有可能找不到应用对象在哪里。故CMS收集器使用标记清除算法而不是使用标记整理算法。

新生代:新建的对象都是用新生代分配内存,比如我们在方法中去new一个对象,那这方法调用完毕后,对象就 会被回收,这就是一个典型的新生代对象。Eden空间不足的时候,会把存活的对象转移到Survivor中,
新生代大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例。
老年代:在新生代中经历了N次垃圾回收后仍然存活的对象就会被放到老年代中。而且大对象直接进入老年代。
永久代:方法区

JVM 性能优化

JVM 常见问题

[参考资料]https://www.cnblogs.com/alsf/p/9388233.html

  1. 对象创建过程
  1. Student s = new Student();在内存中做了哪些事情?
上一篇 下一篇

猜你喜欢

热点阅读