面试复习-JVM

  • Java内存区域与内存溢出异常

 

https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/5778d113-8e13-4c53-b5bf-801e58080b97.png

                                                                  运行时数据区

 

 

 

                   

 

 

 

 

 

对象的创建过程(new

 

 

  • Java内存区域与内存溢出异常

 

https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/5778d113-8e13-4c53-b5bf-801e58080b97.png

                                                                  运行时数据区

 

 

 

                   

 

 

 

 

 

对象的创建过程(new

 

 

 

对象在内存中的布局可以分为三块区域:对象头、实例数据和对齐填充。

对象头有两部分组成:第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳。第二部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的,都需要记录下来。

对齐填充并不是必然存在的,这个只是起占位符的作用,java虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,相当于对象的大小必须是8字节的整数倍。

 

 

对象的访问定位

Java程序需要通过栈上的reference数据来操作堆上的具体对象,reference只是存储了一个指向对象的引用。

主流有两种访问方式:使用句柄和直接指针两种。

使用句柄:就是在java堆中划分出句柄池和实例池,reference中存储的就是对象的句柄地址,句柄中包含了对象实例数据与到类型数据各自的具体地址信息。对象实例数据存储在实例池中,而类型信息存储在方法区中。对象实例数据移动只会改变句柄池中的实例数据指针,而不会改变java栈中的reference。

使用直接指针:java堆对象布局需要处理对象实例数据和到对象类型数据的指针放置问题。Reference中存放的是对象的地址。这样做可以节省一次定位的时间开销、速度快。

Sun HotSpot使用的直接指针,其他运用句柄也很常见。

 

三、垃圾收集器与内存分配策略

判定对象是否存活:引用计数法、可达性分析算法。

引用计数法:为每个对象添加一个引用计数器,每一个地方引用它时,计数器值就加1;当引用失效是,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。主流java虚拟机没有使用这种方法原因:很难解决对象之间相互循环引用的问题。

 

可达性分析算法:通过GC Roots对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

在java语言中,可作为GC Roots的对象包括下面几种:

  1. 虚拟机栈局部变量表中引用的对象。
  2. 方法区中静态变量引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中JNI引用的对象。

 

垃圾收集算法:标记-清除法、复制算法、标记-整理算法、分代收集算法。

标记清除算法(Mark-Swap):先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。标记和清除两个过程效率不高,标记清除之后会产生大量不连续的内存碎片,空间碎片太多会导致分配大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法(Copying):它将可用内存分为三分8:1:1(Eden:survivor:survivor)每次使用Eden和其中一块Survivor。当回收时,将Eden和survivor中还存活的对象一次复制到另一个Survivor空间上,最后清理掉Eden和Survivor空间。

标记整理算法:让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。优点:不会产生内存碎片。不足:需要移动大量对象,处理效率比较低。

分代算法:当前商业虚拟机都采用这种算法。新生代:复制算法。老年代:标记清除算法或标记整理算法。

 

 

 

 

Remembered Sets(RSets)已记忆集合

已记忆集合在每个分区中都存在,并且每个分区只有一个RSet。其中存储着其他分区中的对象对本分区对象的引用,是一种points-in结构。ygc的时候,只要扫描RSet中的其他old区对象对于本young区的引用,不需要扫描所有old区。mixed gc时,扫描Old区的RSet中,其他old区对于本old分区的引用,一样不用扫描所有的old区。提高了GC效率。因为每次GC都会扫描所有young区对象,所以RSet只有在扫描old引用young,old引用old时会被使用。

为了防止RSet溢出,对于一些比较“Hot”的RSet会通过存储粒度级别来控制。RSet有三种粒度,对于“Hot”的RSet在存储时,根据细粒度的存储阀值,可能会采取粗粒度。

这三种粒度的RSet都是通过PerRegionTable来维护内部数据的。

对象在内存中的布局可以分为三块区域:对象头、实例数据和对齐填充。

对象头有两部分组成:第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳。第二部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容,无论是从父类继承下来的,还是在子类中定义的,都需要记录下来。

对齐填充并不是必然存在的,这个只是起占位符的作用,java虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,相当于对象的大小必须是8字节的整数倍。

 

 

对象的访问定位

Java程序需要通过栈上的reference数据来操作堆上的具体对象,reference只是存储了一个指向对象的引用。

主流有两种访问方式:使用句柄和直接指针两种。

使用句柄:就是在java堆中划分出句柄池和实例池,reference中存储的就是对象的句柄地址,句柄中包含了对象实例数据与到类型数据各自的具体地址信息。对象实例数据存储在实例池中,而类型信息存储在方法区中。对象实例数据移动只会改变句柄池中的实例数据指针,而不会改变java栈中的reference。

使用直接指针:java堆对象布局需要处理对象实例数据和到对象类型数据的指针放置问题。Reference中存放的是对象的地址。这样做可以节省一次定位的时间开销、速度快。

Sun HotSpot使用的直接指针,其他运用句柄也很常见。

 

三、垃圾收集器与内存分配策略

判定对象是否存活:引用计数法、可达性分析算法。

引用计数法:为每个对象添加一个引用计数器,每一个地方引用它时,计数器值就加1;当引用失效是,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。主流java虚拟机没有使用这种方法原因:很难解决对象之间相互循环引用的问题。

 

可达性分析算法:通过GC Roots对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。

在java语言中,可作为GC Roots的对象包括下面几种:

  1. 虚拟机栈局部变量表中引用的对象。
  2. 方法区中静态变量引用的对象。
  3. 方法区中常量引用的对象。
  4. 本地方法栈中JNI引用的对象。

 

垃圾收集算法:标记-清除法、复制算法、标记-整理算法、分代收集算法。

标记清除算法(Mark-Swap):先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。标记和清除两个过程效率不高,标记清除之后会产生大量不连续的内存碎片,空间碎片太多会导致分配大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

复制算法(Copying):它将可用内存分为三分8:1:1(Eden:survivor:survivor)每次使用Eden和其中一块Survivor。当回收时,将Eden和survivor中还存活的对象一次复制到另一个Survivor空间上,最后清理掉Eden和Survivor空间。

标记整理算法:让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。优点:不会产生内存碎片。不足:需要移动大量对象,处理效率比较低。

分代算法:当前商业虚拟机都采用这种算法。新生代:复制算法。老年代:标记清除算法或标记整理算法。

 

 

 

 

Remembered Sets(RSets)已记忆集合

已记忆集合在每个分区中都存在,并且每个分区只有一个RSet。其中存储着其他分区中的对象对本分区对象的引用,是一种points-in结构。ygc的时候,只要扫描RSet中的其他old区对象对于本young区的引用,不需要扫描所有old区。mixed gc时,扫描Old区的RSet中,其他old区对于本old分区的引用,一样不用扫描所有的old区。提高了GC效率。因为每次GC都会扫描所有young区对象,所以RSet只有在扫描old引用young,old引用old时会被使用。

为了防止RSet溢出,对于一些比较“Hot”的RSet会通过存储粒度级别来控制。RSet有三种粒度,对于“Hot”的RSet在存储时,根据细粒度的存储阀值,可能会采取粗粒度。

这三种粒度的RSet都是通过PerRegionTable来维护内部数据的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值