学习JVM的个人理解以及整理——自动内存管理机制

java虚拟机所管理的内存区域划分为堆,方法区,虚拟机栈,本地方法栈,程序计数器。

每个虚拟机栈中有一个私有的程序计数器,程序计数器占用很小的一块内存,在执行一个java方法时,记录正在执行的虚拟机字节码的地址。虚拟机栈中有一个栈帧,用于存放局部变量表,操作数栈,动态链表,方法出口等。

常量池用于存放编译期期间生成的各种字面量和符号,在类加载后进入方法区的运行时常量池。

Java语言并不要求常量一定在编译期才能产生。并非一定是在class编译期中预置的才能进去方法区中的运行时常量池,在运行期间也可以将常量放入。运用的最多的就是String类的intern()方法。运行时常量池是方法区的一部分,当无法申请的内存是会抛出OOS异常。

对象的创建,当遇到一个new指令时,会先去检查这个指令的参数在常量池中是否能定位到一个类的符号引用,在检查这个符号引用代表的类是否被加载,解析和初始化够。在类加载通过后,对象所需要的大小可以完全确定。在Java堆中,拥有一个指针座位分界点的指示器,当需要分配内存时,会指向未被分配内存的区域,将其挪动一段与对象大小相等的距离,称为指针碰撞。如果内存不是规整的,会有一个列表,上面记录哪些区域是可用的,当需要是划分列表中一块足够大的空间分配给对象,然后更新列表上的记录,称为空闲列表。Java堆是否规整由GC收集器是否带有压缩整理功能决定。Serial等带有Compact过程的采用指针碰撞,CMS这种基于Mark-Sweep算法的收集器,采用空闲列表。

对象在内存中存储的布局可以分为3部分:对象头,实例数据,对齐填充。

对象头包含两部分,第一部分是自己运行时的数据,包裹哈希吗,GC分代年龄,锁状态的标志,线程自带的锁,偏向线程ID,偏向时间戳等。另一部分是类型指针,指向它的类元数据。虚拟机可以通过这个指针来确定它是哪个类的实例。如果是Java数组的话,在对象头中还必须有一块用于用于记录数组长度的数据,虚拟机可以通过普通java对象的元数据确定Java对象的大小,但是从数组的元数据中无法确定数组的大小。

对齐填充并不是必然存在的,起到一个占位符的作用,HotSpot VM的自动内存管理系统要求对象的起始地址必须是8字节的倍数,当对象的实例数据没有对齐的时候,就需要对齐填充来补全。

实例数据是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。存储顺序受到虚拟机分配策略参数和字段在java源码中定义顺序的影响。相同宽度的字段总是被分配到一起。满足这个条件下,父类中定义的方法会出现在子类之前。如果CompactFields为true,子类中较窄的变量也可能会插入到父类变量的空隙之中。

在jdk1.6之前,StringBuilder会在java堆中创建一个实例,调用String.intern()会把这个实例复制在方法区,所以他们不是一个相同的引用,在jdk1.7后,不会再实现复制,而是在方法区中记录首次出现的实例引用。





猜你喜欢

转载自blog.csdn.net/qq_38256015/article/details/79975328