JVM的细节

Java Heap和方法区的布局

Java Heap的布局一般是下图所示:

这里写图片描述

但是如果使用G1收集器时,Java堆的内存布局与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合:
引自:jvm系列(三):java GC算法 垃圾收集器
这里写图片描述

内存分配与回收策略

引自:《深入理解Java虚拟机:JVM高级特性与最佳实践》3.5节

原则:

  • 一般情况下(大对象例外),新对象在Eden区中分配。当Eden区中不够分配该对象时,JVM将进行一次Minor GC,将Eden区中该回收的对象回收、该挤到Survivor或old区的对象挤走,然后再在Eden区中分配该新对象
  • 超过-XX:PretenureSizeThreshold的大对象直接分配到old区
  • 长期存活的对象将进入old区
  • 如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入old,无须等到MaxTenURingThreshold中要求的年龄
  • 空间分配担保。它的意思是在new区进行了minor GC后还是无法容纳对象(尽力都搞不定),让old区容纳一部分对象(就是责任分担)。为防止old区都容纳不下的情况,所以要先通过之前每次new区晋升到old区的对象平均大小与old区剩余空间大小比对以及HandlePromotionFailure设置是否允许担保失败来决定是否full GC

class的静态文件与运行时

对于:

public class A {
    public final static int i=123;
}

编译成A.class后,利用javap -verbose A进行反编译得到其字节码:

这里写图片描述

在class的静态文件并没有说A.i的内存地址在哪里(只有字段符号引用),只是在cliinit方法中利用其字段符号引用对A.i进行了赋值;而其字段符号引用对应的内存地址(即直接引用)由JVM在运行时进行解析后得到。

下图可以看出类加载过程:
这里写图片描述

总结:
1. 静态变量(没被final修饰的)是在运行时被确定内存位置并赋值的
2. 在静态文件中的符号引用,会在运行时通过解析替换为直接引用

静态变量、字段表、字段符号引用之间关系

  • 静态变量是在运行时才会有其内存地址(直接引用),值保存在里面
  • 字段表集合是用来对外展示类的静态变量信息(并不含有直接引用)
  • 字段符号引用(CONSTANT_Filedref_info)是保存在类的常量池中,用来描述引用的是哪个类的哪个静态变量

对于静态变量:

class A {
    public static int i=123;
}
public class B {
    public static int j=A.i;
    public static void main(String[] args) {

    }
}

A.i是静态变量。它的修饰符public static int放在A类的字段表里。对于B.j来说怎么找到A.i呢?A.i是作为CONSTANT_Filedref_info保存在B类的常量池中。如果CONSTANT_Filedref_info没被解析过,要想获得A.i的值要通过以下步骤(实际就是对符号引用的解析):

  1. 到B类的常量池中获取该CONSTANT_Filedref_info
  2. 通过该CONSTANT_Filedref_info得知其类名为A,字段名为i
  3. 在Java堆中找到A的类对象(如果找不到就要进行A类加载),通过它跳回到方法区中找到A类
  4. 在A类的常量池中解析得到A.i的直接引用
  5. 将B类中该CONSTANT_Filedref_info替换为直接引用(完成CONSTANT_Filedref_info在B类的解析)
  6. 返回该直接引用

【疑问】步骤4的具体过程目前还不清楚,需要查看虚拟机规范

这里写图片描述

对于final修饰的静态变量:

public class A {
    public final static int i=123;
}

根据《深入理解Java虚拟机:JVM高级特性与最佳实践》p166:

而对于类变量,则有两种方式可以选择:
1. 赋值在类构造器方法中进行
2. 使用ConstantValue属性来赋值
目前Sun Javac编译器的选择是:如果同时使用final和static来修饰一个变量(或者说常量更贴切),并且这个变量的数据类型是基本类型或者java.lang.String的话,就生成ConstantValue属性来进行初始化;否则,选择在方法中进行初始化。

上述内容说明了A.i的值123不是在运行时赋值给A.i,在静态文件中就通过字段表的属性表的ConstantValue项进行了值的确定。对A.class反编译得到:

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值