漫谈Java对象

接下来我们来讨论下对象在虚拟机中分配、布局和访问的过程。因为不同虚拟机的实现不同,下文我们以Hot-Spot虚拟机为例。

对象的创建

对象的创建总体过程可以概括为 关键字 -> 类型检查 -> 内存分配 -> 零值 -> 构造函数。

关键字触发

当JVM遇到一条字节码new指令的时候,将触发对象创建的流程。

类型检查

首先JVM会检查该指令的参数能否在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已经被加载、解析和初始化。

内存分配

两种分配方法

JVM的内存分配算法与JVM的GC算法息息相关。当JVM的内存时规整的,此时JVM只需要将内存指针向空闲方向挪动即可,这种方法被称为“指针碰撞”。但如果内存是不规整的,那么JVM就需要维护一个列表,来记录哪些部分是可用的,此方法叫做“空闲列表”。

分配的线程安全

仅考虑如何分配内存是不够的,因为在内存分配中也会涉及到线程安全的问题。在并发情况下,当线程A向对象A分配内存,此时A的指针还没来得及修改,线程B直接将对象B创建到了该位置。

有两种办法来保证线程安全:第一种是采用同步机制,利用CAS(compare and swap)操作来保证操作的原子性,这里拓展一下通常CPU支持CAS操作,但是底层也是通过lock指令实现的。另一种是现在TLAB中分配,当TLAB不足时再同步锁定。

赋零值

实例数据

当内存分配完后,虚拟机需要将分配到的值都初始化为零值,这一步操作的位置同内存分配的位置相关,可能在TLAB中进行。这一步的意义在于保证了对象中的实例字段在java代码中可以不赋初始值就可以直接使用。因此,实例代码块总是在实例对象赋值之后执行。 

public class MyObject {
    int i;
    int j = 1;

    {
        System.out.println("init block start:" +i +" | "+j);
        j = 2;
        System.out.println("init block end:" +i +" | "+j);
    }

    public MyObject(int i, int j) {
        this.i = i;
        this.j = j;
    }

}
public class InitTest {
    public static void main(String[] args) {
        MyObject myObject = new MyObject(10, 20);
        System.out.println("Init finished: "+ myObject.i +" | "+ myObject.j);
    }
    /**
     * Output:
     * init block start:0 | 1
     * init block end:0 | 2
     * Init finished: 10 | 20
     */
}

对象头

接下来JVM还需要对此对象进行设置,例如对象是哪个类、如何找到类的元数据、对象的哈希码、对象的GC分代年龄和是否被用作锁等。(Mark Word + Class Pointer)

构造函数

上面的操作都完成后从JVM的角度看一个对象已经创建完成了,但是从java程序的角度来看对象的创建才刚开始——构造函数。在构造函数中,会执行<init>指令,按照程序员的意愿对对象进行初始化。至此一个完成的对象才算是创建完成了。

对象的内存布局

一个对象在内存中通常由三个部分组成:对象头、示例数据和对齐填充。

对象头

Mark Word

Mark Wrod中保存了对象的状态信息等,具体如下表:

存储内容标志位状态
对象哈希码、对象分代年龄01未锁定
指向锁记录的指针00轻量级锁定
指向重量级锁的指针10重量级锁定
空,不需要记录11GC标记
偏向线程ID、偏向时间戳、对象分代年龄01可偏向

Class Pointer

指向类型元数据的指针。

实例数据

即我们在代码中定义的成员变量,包括父类继承的部分。

对齐填充

Hot-Spot要求对象的大小必须是8字节的整数倍,因此需要对齐填充。

对象的访问定位

Java程序通过栈上的reference数据来访问和操作堆上的具体对象。常见的有下面的两种方式。

句柄

句柄下的访问可以抽象成如下过程:

reference -> 句柄池 -> 实例数据+对象类型数据。

该模式下,reference需要句柄池来做“代理”,因此reference不需要跟踪因GC而引起的数据地址变化,句柄池会做更新。但是因为多了一层,导致访问速度较慢

直接指针

在这种模式下reference直接标记对象的内存地址,好处是速度快。但是需要对reference进行维护更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java安全漫谈是一本关于Java安全的书籍,深入探讨了Java应用程序在网络环境中的安全性和相关的安全漏洞。该书内容涵盖了Java安全基础、Java虚拟机的安全机制、Java安全管理、Java安全开发等方面的知识。 首先,Java安全基础部分介绍了Java安全模型的原理和特点,包括Java类库的安全特性、权限管理和访问控制、安全策略配置等。这部分内容可帮助开发人员了解Java应用程序的安全需求,并提供相应的解决方案。 其次,Java虚拟机的安全机制是Java应用程序的基石。该书介绍了Java虚拟机的安全沙箱和类加载机制,并讨论了如何利用这些安全机制避免恶意代码的执行和隐患的防范。 此外,Java安全管理部分从用户角度出发,介绍了Java应用程序的安全管理工具和技术,如Java安全策略文件、权限管理和安全认证等。开发人员可以通过合理配置和使用这些工具来提高Java应用程序的安全性。 最后,该书还涉及了Java安全开发过程中的一些最佳实践和常见安全漏洞,如输入验证、跨站脚本攻击(XSS)、SQL注入、跨站请求伪造(CSRF)等。通过学习和掌握这些知识,开发人员可以编写出更加安全的Java应用程序。 总而言之,Java安全漫谈是一本全面讨论Java安全的书籍,内容涵盖了Java安全基础、Java虚拟机的安全机制、Java安全管理和Java安全开发等方面的知识。它对于开发人员和安全从业人员来说,都是一本重要的参考书,有助于提高Java应用程序的安全性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值