对象的创建到死亡

JVM下的内存分布:

1.程序计数器:每一个线程都有一个程序计数器,执行java方法时,计数器存放着正在执行的虚拟机字节码指令的地址,执行Native方法时,计数器为空。

2.栈:线程私有,生命周期与线程的生命周期相同,内存空间在编译期完成分配,运行时不会改变。

3.堆:堆是被所有线程共享的一块内存,在虚拟机启动时创建,堆可以在物理上不连续,只要在逻辑上连续即可。

4.方法区:被所有线程共享,用于存储已被虚拟机加载的类信息、常量、静态变量、编译器编译后的代码。JDK1.7后,字符串常量池移除方法区。

5.本地栈:为Nativa方法服务。

6.运行时常量池:每一个Class文件中带有一个运行时常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类的加载后进入方法区的运行时常量池中存放。

虚拟机中对象创建的过程(限于普通Java对象):

1.首先在常量池中找到要创建对象的类的引用,并且检查这个类是否已经被加载、解析和初始化。

2.这个类没有被加载,必须执行相应的类加载过程。

3.类加载通过后,虚拟机为对象分配内存。内存分配方式为指针碰撞或者空闲列表。

4.内存分配完成后,虚拟机将内存空间初始化为“零值”。

5.虚拟机对对象进行设置。(例如:这个对象属于哪个类,对象的哈希码等。这些信息在对象头之中。)

6.将对象按照代码进行初始化。

指针碰撞:在内存绝对规整的情况下,指针分界着用过的内存和空闲内存,指针向空闲内存移动对象大小的空间,被称为指针碰撞。

空闲列表:在内存不规整的抢矿下,虚拟界维护一个列表,记录着那些内存可用的,在列表中找出够大的空间存放对象,被称为空闲列表。

对象的内存布局

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

对象头包含两部分:1.存储对象之神的运行是数据,如哈希码等。2.类型指针,虚拟机通过这个指针来确定这个对象是哪个类的实力。

实例数据:代码中定义的各种字段。

对齐填充:保证对象的大小是8字节的倍数。

对象的访问定位

通过栈上的reference数据来操作栈上的具体对象,访问方式有使用句柄和直接指针两种。

使用句柄:堆中会划分一块句柄池,reference中存储的是对象的句柄地址,句柄池中的句柄中包含了对象的示例数据和类型数据的地址信息。

直接访问:reference中存放的是对象实例数据的地址,在实例数据中有对象类型数据的地址。

各自的优点:

句柄优点为:在对象移动的时候,只会该病句柄池中的句柄,不会改变reference数据本身。

直接访问优点:速度快。

怎样判定对象是否已经“死亡”?

1.引用计数法:给对象添加一个引用计数器,每当有一个地方引用这个对象时,计数器的值+1,否则计算器的值-1,任何时候,计数器的值为0,对象死亡。

缺点:很难解决对象之间互相引用的问题。

2.可达性分析算法:设置一个起始点,从起始点开始向下搜索,搜索路径被称为引用链,当一个对象到起始点之间没有一条完整的引用链时,对象死亡。

可以作为起始点的对象为:

栈中引用的对象、方法区中的静态对象、方法区中的常量对象、本地栈中的对象。

引用的分类:

强引用:代码中存在的引用,引用在垃圾回收器不会回收引用的对象。

软引用:还有用但非必要的对象,在放生内存溢出异常之前,垃圾回收器将这些对象进行第二次回收。

弱引用:也是还有用但非必要的对象,但是比软引用还要弱,垃圾啊及回收期工作时,就会对这些对象进行回收

虚引用:最弱的一中引用,无法通过虚引用老火的一个实例,对象被回收时会有系统通知。

猜你喜欢

转载自www.cnblogs.com/xiaosuye/p/10269193.html
今日推荐