Java 面试复习_6

本文详细介绍了Java中对象创建的步骤,包括内存分配的指针碰撞和空闲列表两种方式,以及多线程环境下内存分配的安全性问题。接着讨论了对象的内存布局,包括对象头、实例数据和对齐填充。最后,文章阐述了垃圾收集算法,如标记清除、复制、标记整理,以及Java中对象回收的可达性分析算法和引用类型。还提及了`finalize`方法的执行流程及其潜在风险。
摘要由CSDN通过智能技术生成

2019-5-29
作者:水不要鱼

(注:能力有限,如有说错,请指正!)

  • new 指令

当我们使用 new 指令去创建一个对象的时候,主要会经历以下几个阶段:

  1. 检查常量池中是否有这个类的符号引用
  2. 检查这个符号引用指向的类是否已经被加载、解析以及初始化,如果没有的话就需要去执行加载的一系列工作了
  3. 为对象分配内存空间,当空间分配好之后还需要将空间初始化为零值,
  4. 当以上的工作都执行完毕了,就会执行 <init> 方法,将对象以我们的意愿进行初始化

这里面有两个点需要注意,都是和分配内存有关,一个是如何分配出一块内存,另一个就是多线程下内存分配的安全性。

  • 如何分配出对象所需的内存空间

在 JVM 中存在两种内存分配方式:指针碰撞和空闲列表。如果内存是规整的,也就是说空闲区域和已使用区域刚好是分成两块区域的时候,
使用一个指针代表这两个区域的分界线就可以了,当需要分配一块内存空间时,直接将这个指针移动一段和这个空间大小相等的距离即可,这就是
指针碰撞。如果内存不是规整的,也就是空闲区域和已使用区域有很多块零散的,夹杂在一起,就需要用一张表来记录空闲的区域有哪些,在哪里,
当需要分配一块内存空间时,就从表上找出大小相当的一块或者几块区域来使用,这就是空闲列表。

很明显,指针碰撞所使用的方式在分配时速度是很快的,因为只需要更新一个指针的位置就可以了,但是想要使用指针碰撞的前提是
内存是规整的,这就要求垃圾收集器要有压缩整理的功能,将内存整理为规整的。

  • 多线程下内存分配的安全性问题

由于对象分配内存空间时用的内存区域是共享的,假设我们使用指针碰撞,当一个对象已经在分配内存了,但是指针还没修改,
这时候又来了一个对象分配内存,还是使用的原来的指针,这就会出现多线程问题。多线程问题一般就是两种解法,同步处理和隔离处理。
同步处理很好理解,就是让所有操作同步执行,一个对象创建完才创建另外一个,有点像排队,在 JVM 中是采用 CAS 加失败重试的措施来保证同步的。
隔离处理也很好理解,既然这个问题是操作共享内存才会出现,那我们让他们操作的是不同的区域不就好了,当然,这就要求每个线程都要有自己的一份空间,
这个空间称为 TLAB (Thread Local Allocation Buffer)。这个方式是否开启,可以通过 JVM 参数来设置:-XX:+/-UseTLAB。

对象的内存布局

对象在 JVM 中的内存布局分为了 3 个区域:对象头实例数据对齐填充

  1. 对象头:一般包含两个数据,对象自身运行时的数据(Mark Word)和类型指针。如果是数组对象,就还会多一个数据用于记录数组的大小。
  2. 实例数据:就是我们在类中定义的数据,通常情况下,父类的数据会在子类前面,但是也有可能出现父类的变量中夹杂着一个子类的变量,
    这是在开启 CompactFields 之后会发生的事情。而且,在 JVM 中,相同宽度的字段会被放在一起,这一方面是为了内存的管理,另一方面也是为了性能。
    值得一提的是,byte 和 boolean 是一起的,也就是说 boolean 也是占一个字节,这点否决了一些人说的 boolean 占 1bit 的观点。
  3. 对齐填充:还是为了内存管理和性能,JVM 规定对象起始地址必须是 8 的整数倍&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值