7、jvm

元空间与永久代 方法区
永久代是堆的一部分
永久代最终被移除,方法区移至Metaspace,字符串常量移至Java Heap
元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。
classLoader(双亲委派)加载部分.class->类信息放入元空间->生成唯一Class对象放进堆内存

《Java虚拟机规范》只是规定了有方法区这么个概念和它的作用,并没有规定如何去实现它。
永久代是HotSpot的概念,方法区是Java虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现。
对于Java8, HotSpots取消了永久代,那么是不是也就没有方法区了呢?当然不是,方法区是一个规范,规范没变,它就一直在。那么取代永久代的就是元空间。它可永久代有什么不同的?存储位置不同,永久代物理是是堆的一部分,和新生代,老年代地址是连续的,而元空间属于本地内存;存储内容不同,元空间存储类的元信息,静态变量和常量池等并入堆中。相当于永久代的数据被分到了堆和元空间中。

字面量 符号引用 直接引用 Class文件常量池 运行时常量池 字符串常量池
Class文件常量池:存字面量 符号引用,类被加载后放在方法区的运行时常量池 (存在永久代 后元空间)
运行时常量池:运行时常量池可以在运行期间将符号引用解析为直接引用。
字符串常量池:intern JVM 中除了字符串常量池,8种基本数据类型中除了两种浮点类型剩余的6种基本数据类型的包装类,都使用了缓冲池技术,但是 Byte、Short、Integer、Long、Character 这5种整型的包装类也只是在对应值在 [-128,127] 时才会使用缓冲池,超出此范围仍然会去创建新的对象。

垃圾回收GC

  • 内存分配方式
    指针碰撞(已使用内存区域与空闲内存区域的分隔点)
    空闲列表(专门维护一个列表,记录哪些内存块可用)
    Serial、ParNew等压缩整理过程的收集器就使用“指针碰撞”,基于CMS这种清除算法就使用“空闲列表”。
    TLAB:本地线程分配缓冲
  • 对象访问方式 栈上的reference数据
    句柄:句柄池,垃圾回收,对象移动时,reference不用修改,只用修改句柄池中指向的地址。
    直接指针:速度快。节省一次定位,HotSpot

java.lang.OutOfMemoryError: Java heap space 堆
java.lang.OutOfMemoryError: PermGen space 永久代
java.lang.OutOfMemoryError: Metaspace 元空间

吞吐量暂停时间,两个垃圾回收最重要的点:
吞吐量可以集中某个时间垃圾回收:不要求实时,
暂停时间少 垃圾回收分散在某个范围的各个时间段:快速响应
现在标准: 在最大吞吐量优先的情况下,降低停顿时间。
算法:标记清理、复制、标记清除
具体策略:分代
实现:新生代一种垃圾回收器,老年代一种回收器

初始标识、并发标识、再标记、并发清理
串行回收器、并行回收器、并发回收器

CMS:最短停顿时间 初始标记->并发标记->重新标记->并发清理->并发重置 吞吐量低
G1:标记整理算法 初始标记,并发标记,最终标记,筛选回收。可以精确地控制停顿。
ParNew回收器
Par是Parallel的缩写,New:只能处理的是新生代
HotSpot的年轻代中除了拥有ParNew收集器是基于并行回收的以外,Parallel Scavenge收集器同样也采用了复制算法、并行回收和Stop-the-world机制。

① 和ParNew收集器不同,Parallel Sscavenge收集器的目标则是达到一个可控制的吞吐量(Throughput),它也被称为吞吐量优先的垃圾收集器。
② 自适应调节策略也是Parallel Scavenge与ParNew一个重要区别。

在程序吞吐量优先的应用场景中,Parallel收集器和Parallel Old收集器的组合,在server模式下的内存回收性能很不错。
在Java8中,默认是此垃圾收集器。

在JDK 1.5时期,HotSpot推出了一款在强交互应用中几乎可认为有划时代意义的垃圾收集器:CMS (Concurrent-Mark-Sweep)收集器,这款收集器是HotSpot虚拟机中第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程同时工作。
CMS收集器的关注点是尽可能缩短垃圾收集时用户线程的停顿时间。停顿时间越短(低延迟)就越适合与用户交互的程序,良好的响应速度能提升用户体验。
目前很大一部分的Java应用集中在互联网站或者B/s系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验CMs收集器就非常符合这类应用的需求。
CMS的垃圾收集算法采用标记-清除算法,并且也会Stop-the-World机制。

不幸的是,CMS作为老年代的收集器,却无法与JDK 1.4.0中已经存在的新生代收集器Parallel scavenge配合工作,所以在JDK 1.5中使用CMS来收集老年代的时候,新生代只能选择ParNew或者Serial收集器中的一个。在G1出现之前,CMS使用还是非常广泛的。一直到今天,仍然有很多系统使用CMS GC。

-XX:+UseConcMarkSweepGC 手动指定使用CMS收集器执行内存回收任务。
✏️开启该参数后会自动将 -XX:+UseParNewGC 打开。即: ParNew ( Young区
用)+CMS (old区用)+serial Old的组合。
🥈CMS的弊端
①会产生内存碎片,导致并发清除后,用户线程可用的空间不足。在无法分配大对象的情况下,不得不提前触发Full GC。
②CMS收集器对CPU资源非常敏感。在并发阶段,它虽然不会导致用户停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。
③CMS收集器无法处理浮动垃圾。可能出现“Concurrent Mode Failure"失败而导致另一次 Full GC 的产生。在并发标记阶段由于程序的工作线程和垃圾收集线程是同时运行或者交叉运行的,那么在并发标记阶段如果产生新的垃圾对象,CMS将无法对这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有被及时回收,从而只能在下一次执行GC时释放这些之前未被回收的内存空间。

①如果你想要最小化地使用内存和并行开销,请选Serial GC;
②如果你想要最大化应用程序的吞吐量,请选Parallel GC;
③如果你想要最小化GC的中断或停顿时间,请选CMS GC。

JDK9新特性:CMS被标记为Deprecate了(JEP291)
✏️如果对JDK9及以上版本的HotSpot虚拟机使用参数-XX:+UseconcMarkSweepGC来开启CMS收集器的话,用户会收到一个警告信息,提示CMS未来将会被废弃。

JDK14新特性:删除CMS垃圾回收器(JEP363)
✏️移除了CMS垃圾收集器,如果在JDK14中使用-XX:+UseConcMarkSweepGC的话,JVM不会报错,只是给出一个warning信息,但是不会exit。JVM会自动回退以默认GC方式启动JVM。

并行:多cpu一起
并发:垃圾回收和应用程序同步运行

CMS与G1垃圾回收器
CMS是老年代回收器,只能和ParNew新生代回收器配合使用
G1是全年龄代回收:

java类初始化
Java 虚拟机规范没有强制约束类加载过程的第一阶段(即:加载)什么时候开始,但对于“初始化”阶段,有着严格的规定。有且仅有 5 种情况必须立即对类进行“初始化”:

在遇到 new、putstatic、getstatic、invokestatic 字节码指令时,如果类尚未初始化,则需要先触发其初始化。
对类进行反射调用时,如果类还没有初始化,则需要先触发其初始化。
初始化一个类时,如果其父类还没有初始化,则需要先初始化父类。
虚拟机启动时,用于需要指定一个包含 main() 方法的主类,虚拟机会先初始化这个主类。
当使用 JDK 1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果为 REF_getStatic、REF_putStatic、REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类还没初始化,则需要先触发其初始化。
这 5 种场景中的行为称为对一个类进行主动引用,除此之外,其它所有引用类的方式都不会触发初始化,称为被动引用。

准备阶段是正式为类变量(或称“静态成员变量”)分配内存并设置初始值的阶段。这些变量(不包括实例变量)所使用的内存都在方法区中进行分配。

初始值“通常情况下”是数据类型的零值(0, null…),假设一个类变量的定义为:

public static int value = 123;
那么变量 value 在准备阶段过后的初始值为 0 而不是 123,因为这时候尚未开始执行任何 Java 方法。

存在“特殊情况”:如果类字段的字段属性表中存在 ConstantValue 属性,那么在准备阶段 value 就会被初始化为 ConstantValue 属性所指定的值,假设上面类变量 value 的定义变为:

public static final int value = 123;
那么在准备阶段虚拟机会根据 ConstantValue 的设置将 value 赋值为 123。

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

类初始化阶段是类加载过程的最后一步,是执行类构造器 () 方法的过程。

() 方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static {} 块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的。

() 方法不需要显式调用父类构造器,虚拟机会保证在子类的 () 方法执行之前,父类的 () 方法已经执行完毕。

由于父类的 () 方法先执行,意味着父类中定义的静态语句块要优先于子类的变量赋值操作。

java类初始化顺序
Parent static block
Child static block
Parent instance block
Parent constructor
Child instance block
Child constructor

1 parent静态属性初始化
2 parent静态方法块初始化
3 child静态属性初始化
4 child静态方法块初始化
5 parent普通属性初始化
6 parent普通方法块初始化
7 parent构造函数初始化
8 child普通属性初始化
9 child普通方法块初始化
10 child构造函数初始化
11 ================================
12 parent普通属性初始化
13 parent普通方法块初始化
14 parent构造函数初始化
15 child普通属性初始化
16 child普通方法块初始化
17 child构造函数初始化

1.初始化父类静态变量
2.初始化父类的静态代码块
3.初始化子类的静态变量
4.初始化子类的静态代码块
5.父类的非静态变量
6.父类的非静态代码块
7.父类的构造函数
8.子类的非静态变量
9.子类的非静态代码块
10.子类的构造函数

参考文档
Metaspace 之一:Metaspace整体介绍(永久代被替换原因、元空间特点、元空间内存查看分析方法)

深入理解堆外内存 Metaspace

Java方法区、永久代、元空间、常量池详解

Java垃圾回收器 用户:WYSCODER

史上最经典垃圾回收器(CMS,G1)详解、适用场景及特点、使用命令

JVM

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值