JVM笔记

目录

4.3 对象的创建过程

4.4 对象在内存中的存储布局

1. 观察虚拟机配置

2.1 普通对象

2.2 数组对象

4.6 对象头

对象怎么定位

对象怎么分配

4.8 运行时数据区

方法区

4.9 栈帧

栈溢出

字节码分析

5.1 invoke指令

5.3 垃圾

如何找到垃圾

5.4 垃圾清除算法

1. 标记-清除

2. 拷贝

3. 标记压缩

5.5 堆内逻辑分区

5.6 栈上分配

5.7 对象何时进入老年代

常见的垃圾回收器

组合1:S+SO:不常用已被淘汰

组合2:PS+PO:生产环境默认组合

组合3:PN+CMS

PN vs PS

CMS

CMS的问题

算法

Card Table

G1

CollectionSet

RememberedSet

MixedGC

三色标记算法


JVM_阿狸男朋友的博客-CSDN博客

4.3 对象的创建过程

>class loading:
    1. 将class文件load到内存
>class linking:
    1. Verification  验证文件是否符合JVM规定 CAFE BABE
    2. Preparation  静态成员变量赋默认值
    3. Resolution 将类、方法、属性等符号引用解析为直接引用,常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用
>class initializing:
    1.调用类初始化代码 <clinit>,给静态成员变量赋初始值 (<clinit>是静态语句块,<init>是构造方法)
>开始new对象:
>申请对象内存
>对象成员变量赋默认值 (t=0)
>调用构造方法<init>
    1.成员变量按顺序赋初始值 (t=8)
    2.执行构造方法语句
        2.1 初始化super
        2.2 执行自己的构造方法语句

4.4 对象在内存中的存储布局

1. 观察虚拟机配置

java -XX:+PrintCommandLineFlags -version

C:\Users\Administrator>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=400886336 
-XX:MaxHeapSize=6414181376 
-XX:+PrintCommandLineFlags 
-XX:+UseCompressedClassPointers 
-XX:+UseCompressedOops 
-XX:-UseLargePagesIndividualAllocation 
-XX:+UseParallelGC
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

2.1 普通对象

    1. 对象头:markword  8字节
    2. ClassPointer指针:指向xx.class文件在内存中的地址
        若虚拟机开启参数-XX:+UseCompressedClassPointers则占4字节,不开启为8字节
    3. 实例数据:如对象的成员变量int m=8中的m,String s = "xx",s指向另一个地址
        1. 基本类型:如int占4字节,byte占1字节
        2. 引用类型:如String或其他对象类型,若开启-XX:+UseCompressedOops 为4字节 不开启为8字节
           Oops: Ordinary Object Pointers
    4. Padding对齐,为保证对象总大小是8的倍数,进行位数补齐

2.2 数组对象

    1. 对象头:markword 8字节
    2. ClassPointer指针同上 4/8字节
    3. 数组长度:4字节
    4. 数组数据
    5. 对齐 8的倍数

public class T03_SizeOfAnObject {
    public static void main(String[] args) {
        System.out.println(ObjectSizeAgent.sizeOf(new Object()));//8+4+对齐4=16
        System.out.println(ObjectSizeAgent.sizeOf(new int[] {}));//8+4+4=16
        System.out.println(ObjectSizeAgent.sizeOf(new P()));// 32
    }

    //一个Object占多少个字节
    // -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
    // Oops = ordinary object pointers
    private static class P {
                        //8 _markword
                        //4 _class pointer
        int id;         //4
        String name;    //4
        int age;        //4
        byte b1;        //1
        byte b2;        //1
        Object o;       //4
        byte b3;        //1
    }
}

4.6 对象头

至少包括3位的锁信息,一位是否偏向锁,2位的锁标志位,GC的标记(分带年龄)

看对象的状态来真正分配这64位

1. hashcode部分

   31位hashcode->system.identityHashCode(...)
    按原始内容计算的hashcode,重写过的hashcode方法计算的结果不会放在这里
         如果对象没有重写hashcode方法,那么默认是调用os::random产生hashcode,可以调用System.identityHashCode获取
         os::random产生hashcode的规则为next_random=(16807seed)mod(2*31-1),因此可以使用31位存储;另外一旦生成了hashcode,JVM会将其记录在markword中
    当调用为重写的hashcode方法以及System.identityHashCode的时候
    GC年龄默认为15,原因是分带年龄占4位,最多到15
当一个对象计算过identityHashCode之后,不能进入偏向锁状态

对象怎么定位

访问对象两种方式--句柄和直接指针_进阶的科技花园~-CSDN博客_句柄和直接指针

  1. 句柄池
  2. 直接指针

对象怎么分配

4.8 运行时数据区

方法区

方法区存储class文件

1. Perm Space (<1.8)
   字符串常量位于PermSpace
   FGC不会清理
   大小启动的时候指定,不能变
2. Meta Space (>=1.8)
   字符串常量位于堆
   会触发FGC清理
   不设定的话,最大就是物理内存

4.9 栈帧

1. Frame - 每个方法对应一个栈帧
   1. Local Variable Table 局部变量表
   2. Operand Stack 操作数栈
      对于long的处理(store and load),多数虚拟机的实现都是原子的
      jls 17.7,没必要加volatile
   3. Dynamic Linking 动态链接
       https://blog.csdn.net/qq_41813060/article/details/88379473 
      jvms 2.6.3
   4. return address 返回地址
      a() -> b(),方法a调用了方法b, b方法的返回值放在什么地方

栈溢出

虚拟机栈中存储每个方法的栈帧,当方法结束后这个栈帧就释放了,如果方法一直不结束就会一直占用虚拟机栈空间

字节码分析

int i = 8:
    bipush 8: byte 8有符号扩展为int 8,压入main方法的操作数栈
    istore_1: 将int 8从操作数栈出栈,将局部变量表下标1位置赋值为8

i = i++:
    iload_1: 将局部变量表1位置的数取出,压入操作数栈[即完成读取i操作,i=8]
    iincr 1 by 1: 将局部变量表1位置的数+1[即完成i++操作,局部变量表中i=9]
    istore_1:将8弹出操作数栈,并赋给局部变量表1位置,最终局部变量表i=8
i = ++i:
    iincr 1 by 1: 将局部变量表1位置的数+1[即完成i++操作,局部变量表中i=9]
    iload_1: 将局部变量表1位置的数取出,压入操作数栈[即完成读取i操作,i=9]
    istore_1:将9弹出操作数栈,并赋给局部变量表1位置,最终局部变量表i=9

5.1 invoke指令

1. InvokeStatic
2. InvokeVirtual
3. InvokeInterface
4. InovkeSpecial
   可以直接定位,不需要多态的方法
   private 方法 , 构造方法
5. InvokeDynamic
   JVM最难的指令
   lambda表达式或者反射或者其他动态语言scala kotlin,或者CGLib ASM,动态产生的class,会用到的指令

5.3 垃圾

如何找到垃圾

1. 引用计数RC(reference count),不能解决循环引用的垃圾团

 

2. 根可达算法RS(root searching)

5.4 垃圾清除算法

1. 标记-清除

适合老年代回收

2. 拷贝

适合新生代回收

3. 标记压缩

5.5 堆内逻辑分区

 

5.6 栈上分配

5.7 对象何时进入老年代

常见的垃圾回收器

JDK诞生时产生Serial 
-> 为了提高回收效率诞生了PS 
-> 为了缩短stop the world的时长, 诞生并发回收器CMS
    CMS是1.4版本后期引入,CMS是里程碑式的GC,它开启了并发回收的过程,
    但是CMS毛病较多,因此目前没有任何一个JDK版本默认是CMS
-> 为了配合CMS,诞生了PN

组合1:S+SO:不常用已被淘汰

组合2:PS+PO:生产环境默认组合

10G内存 PS+PO停顿时间

组合3:PN+CMS

PN vs PS

CMS

CMS的问题

1. Memory Fragmentation【内存碎片化】

内存碎片化可通过设置压缩来解决,代价是这个压缩过程会损失响应时间

-XX:+UseCMSCompactAtFullCollection 每次FGC时都进行压缩

-XX:CMSFullGCsBeforeCompaction 默认为0 指的是经过多少次FGC才进行压缩

2. loating Garbage【浮动垃圾】

Concurrent Mode Failure 产生:
1.并发收集器无法在老年代用完之前完成垃圾对象的回收
    并发清理阶段会产生新的垃圾,这些垃圾就是浮动垃圾,若果老年代满了但是这些浮动垃圾还没来得及清理,这时请出SO单线程回收
2.老年代中无法满足升代所需的内存,产生PromotionFailed问题,则触发STW,用户线程全停止,专门进行垃圾回收
    CMS设计之初只为应付最多10G的内存,32G内存当old区满了,新生代年龄升级要往老年代放的时候,老年代满了
    这是CMS会召唤SO进行单线程的回收,由于内存过大,导致这个STW过程很慢
解决方案:
降低触发CMS的阈值,保持老年代有足够的空间 –XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值,尽早触发CMS清理,让CMS保持老年代足够的空间

算法

Card Table

由于做YGC时,需要扫描整个OLD区,效率非常低,所以JVM设计了CardTable, 如果一个OLD区CardTable中有对象指向Y区,就将它设为Dirty,下次扫描时,只需要扫描Dirty Card 在结构上,Card Table用BitMap来实现

 并发标记算法-三色标记算法

G1

吞吐量方面,G1比PS降低10%-15%,但响应时间/暂停时间可以降低到200ms

追求响应时间用G1,追求吞吐量用PS

CollectionSet

RememberedSet

MixedGC

如果G1产生FGC,你应该做什么?
    1. 扩内存
    2. 提高CPU性能(回收的快,业务逻辑产生对象的速度固定,垃圾回收越快,内存空间越大)
    3. 降低MixedGC触发的阈值,让MixedGC提早发生(默认是45%)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值