JVM

java内存区域

 

 

 程序计数器:

 

 

java虚拟机栈:

 实际上,Java 虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。

 

 

 

 

 

 

 

 

堆: 

 

 

 

 

 

 

 

 

为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?

1、整个永久代有一个 JVM 本身设置的固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。

2、元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 MaxPermSize 控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多了。

2.6 运行时常量池

运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池表(用于存放编译期生成的各种字面量和符号引用)

 

 

2.7 直接内存

直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 错误出现。

三 HotSpot 虚拟机对象探秘

注意java创建对象过程和类加载过程不同。

类加载过程:加载、验证、准备、解析、初始化。

 

 

Step1:类加载检查

虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

Step2:分配内存

类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来。分配方式“指针碰撞”“空闲列表” 两种,选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定

选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的

 

 

 

 

Step3:初始化零值

内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。

Step4:设置对象头

初始化零值完成之后,虚拟机要对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息。 这些信息存放在对象头中。(其实就是ObjectMonitor信息) 另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

Step5:执行 init 方法

在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生了,但从 Java 程序的视角来看,对象创建才刚开始,<init> 方法还没有执行,所有的字段都还为零。所以一般来说,执行 new 指令之后会接着执行 <init> 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

 

 

 

 

 

 

 

 

 

 

 

 

 

 JVM垃圾回收

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

垃圾收集器重点:CMS、G1、ZGC(新一代垃圾收集器),一定要熟练他们的实现原理。

CMS第四步并发清理由于采用的是标记-清楚算法,只需要对需要清楚的对象进行清楚,所以可以和用户程序并发执行。而且CMS堆还是传统连续的堆,对象即垃圾清理的基本单位。

 

 

 G1(很重要!)

参考:https://www.bilibili.com/video/BV13J411g7A1?from=search&seid=4419926641631263984&spm_id_from=333.337.0.0

G1内存结构:由传统的堆结构变成了由很多region组成的,并且E、S、O区并不是连续放在一起的,每个Region大小1~32MB,可以由JVM参数设置,大概2000个左右。

如果对象>=0.5Region  <1Region 会直接放到Old区,如果>1Region,会申请连续的多个Region作为Old区的Region。并标记为H区(Old区的一种)。

 

 

 

 

 

 MixGC  (所有年轻代和部分老年代(直接或间接引用了RootRegion的老年代Region))

分为五步:相比于CMS来说,第一步除了扫描GCRoot对象,还多扫描了RootRegin对象。多了第二步RootReginScan。第四步使用SATB,第五步清理因为采用的复制算法,所以会STW。

 

 第五步清理old区:只清理垃圾比较多的regin里的对象进行清理。垃圾清理old区是没清理干净的,但是清理时间会比较短。

 类加载过程

 

 

 

 

 解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。

 

 

 

 类加载器详解

 

双亲委派:

每一个类都有一个对应它的类加载器。系统中的 ClassLoader 在协同工作的时候会默认使用 双亲委派模型 。即在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派给父类加载器的 loadClass() 处理,当父类加载器为 null 时,会使用启动类加载器 BootstrapClassLoader 作为父类加载器,因此所有的请求最终都应该传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。

 

 

 

 

 

 

 

 

 jdk7之后方法区被元空间所代替。

堆内存中存放的是对象,垃圾收集就是收集这些对象然后交给GC算法进行回收。非堆内存其实我们已经说过了,就是方法区。在1.8中已经移除永久代,替代品是一个元空间(MetaSpace),最大区别是metaSpace是不存在于JVM中的,它使用的是本地内存。 

 

 

 

 

 

 

 

 

 

 

 

整理自:https://javaguide.cn/

 

posted on 2021-12-20 20:45  nisico  阅读(31)  评论(0)    收藏  举报

导航