简述

今天继续写《深入理解java虚拟机》的对象创建的理解。这次和上次隔的时间有些长,是因为有些东西确实不好理解,就查阅各种资料,然后弄明白了才来做记录。

(此文中所阐述的内容都是以HotSpot虚拟机为例的。)

对象的创建

java程序在运行过程中无时无刻都有对象被创建出来,那么创建对象是个怎么样的过程呢?还是看看我自己的理解吧。

判断是否已经执行类加载

当虚拟机遇到一条new指令时 ,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过,如果没有,那必须先执行相应的类加载过程。(类加载过程,以后我也会单独的介绍)

内存分配

当已经执行过类加载过程后,会为新对象在Java堆中分配一个大小已经确定的内存,具体的内存分配规则有两种:

  1. 指针碰撞(Bump the Pointer)
    如果Java堆中的内存是绝对规整的,所有用过的内存放一边,空闲的内存放到一边,中间放着指针为分界点,分配内存就是把指针向空闲的一边挪动一段与对象大小相等的距离。
  2. 空闲列表 (Free List )
    如果Java堆中的内存并不是规整对的,已使用的内存和空间相互交错,虚拟机会将可以用的内存维护到一个列表上,在分配内存时从这个列表中找到一块足够大的空间划给对象。然后更新列表记录。

Java堆中的内存是否是规整的是根据虚拟机所采用的垃圾收集器是否带有压缩整理功能决定的。Serial、ParNew带压缩整理的分配内存用指针碰撞,CMS这种通常用空闲列表方式分配内存(垃圾收集器我也会单独介绍的,看来对象创建涉及的地方很多呢。)

防止并发

在虚拟机上创建对象是非常频繁的行为,所以要做到防止并发,有以下两种方式可实现:

  1. 堆分配内存空间的动作进行同步处理,实际上JVM采用CAS(Cmpare And Set)配上失败重试的方式保证更新操作的原子性;
  2. 把内存分配的动作按照线程划分在不同的空间之中进行,即为每个线程在java堆中预先分配一块小内存,称为本地线程分配缓冲区(Thread Local Allocation Buffer,TLAB)。分配内存时在线程的TLBA上分配,只有TLAB用完并分配新的TLAB时,才需要同步锁定。JVM是否使用TLAB可以通过-XX:+UseTLAB参数来设定。

初始化对象内存空间

内存分配完成后,JVM将分配到的内存空间都初始化为零值(不包括对象头)。

对象头的设置

将对象的类、哈希码、对象的GC分代年龄等信息设置到对象头之中。

执行Java的init方法

设置完对象头后,从JVM的角度来看一个对象已经完成了,但是从java程序的角度来看还没有创建完成呢。此时就需要执行init方法,调用构造方法等过程,这样一个真正可用的对象才算完全的产生出来。

对象的内存布局

创建完对象后,对象对分配给自己的内存是如何布局的呢?下面来介绍一下。

对象在堆内存中的布局可分为三部分:对象头(Header)实例数据(Instance Data)对齐填充(Padding)

对象头:对象头包含两部分,第一部分存储自身运行时数据,如哈希码,GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳等,官方称为“Mark Word”。

第二部分是类型指针,即对象指向它的类元数据的指针,通过此指针来确定是哪个类的对象。

实例数据:存储对象中的各类型的字段内容。无论是从父类继承来的还是在子类中定义的。

对齐填充:并不是必然存在的,当对象实例数据部分没有对齐时,进行对齐补全。

对象的访问定位

Java程序需要通过栈上的reference数据来操作堆上的具体对象。reference数据只是一个指向对象的引用,具体的对象访问根据不同虚拟机有不同的实现,主流的访问方式有两种:使用句柄直接指针

使用句柄:

如果通过句柄来访问对象,Java堆中会划出一块内存作为句柄池,reference中存储句柄地址,而句柄中包含对象的实例数据与类型数据各自的地址。这样就能访问到对象了。

直接指针:

直接指针,就是指reference中直接存储对象的地址。但是Java堆对象的布局中就必须考虑如何防止访问类型数据相关信息。

这两种对象访问方式,各有优势,但是HotSpot使用的是指针对象访问,但是句柄访问对象在整个软件开发范围中也是十分常见的。

参考

《深入理解Java虚拟机》

jvm学习记录-对象的创建、对象的内存布局、对象的访问定位的更多相关文章

  1. Java对象(创建过程、内存布局、访问方法)

    (Java 普通对象.不包括数组.Class 对象等.) ​ 对象创建过程 类加载 遇到 new 指令时,获取对应的符号引用,并检查该符号引用代表的类是否已被初始化.如果没有就进行类加载. 分配内存 ...

  2. JVM学习记录-对象已死吗

    前言 先来回顾一下,在jvm运行时数据区,分为两部分,一个部分是线程共享区,主要包括堆和方法区,另一部是线程私有区分包括本地方法栈,虚拟机栈和程序计数器.在线程私有部分的三个区域是随着线程生和灭的.栈 ...

  3. JVM学习记录

    本博客是为了自己学习JVM而建立,只记录一些自己学习的经过. 最近在看<深入理解Java虚拟机>这本书,里面的内容,很是乏味,因为看不懂所以就会觉得很枯燥,觉得很枯燥看着看着就犯困,然后就 ...

  4. JVM学习记录-线程安全与锁优化(二)

    前言 高效并发是程序员们写代码时一直所追求的,HotSpot虚拟机开发团队也为此付出了很多努力,为了在线程之间更高效地共享数据,以及解决竞争问题,HotSpot开发团队做出了各种锁的优化技术常见的有: ...

  5. JVM学习记录-垃圾回收算法

    简述 因为各个平台的虚拟机的垃圾收集器的实现各有不同,所以只介绍几个常见的垃圾收集算法. JVM中常见的垃圾收集算法有以下四种: 标记-清除算法(Mark-Sweep). 复制算法(Copying). ...

  6. Java基础(2)面向对象和封装,对象的创建和使用、java对象的内存图

    1 类和对象 类:是一类事物的描述,抽象的.猫 对象:是一类事物的实例,具体的.某只猫 2 类的定义 成员变量和成员方法 //定义一个学生类 public class Student { //成员变量 ...

  7. JVM学习记录-线程安全与锁优化(一)

    前言 线程:程序流执行的最小单元.线程是比进程更轻量级的调度执行单位,线程的引入,可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源(内存地址.文件I/O等),又可以独立调度(线程是C ...

  8. JVM学习记录-类加载的过程

    类的整个生命周期的7个阶段是:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(Us ...

  9. 【JVM学习笔记二】垃圾收集器与内存分配策略

    1. 概述 1) GC的历史比Java久远 2) GC需要完成的三件事: | 哪些内存需要回收 | 什么时候回收 | 如何回收 3) Java内存运行时区域各个部分: | Java虚拟机栈.计数器.本 ...

随机推荐

  1. selenium +python+windows 环境搭建

    很久不弄selenium了,好怀念,现在搭建下环境 1,先安装pip ,因为装的是python3,所以只要你不是在渣渣网站下载到坏的版本,在scripts目录下都有pip.exe文件 直接在环境变量里 ...

  2. php和apache工作原理?

    1.CGI和FastCGI是apache处理php脚本的其中两种工作模式,还有ISAPI,SAPI等 2.而php-fpm并不是一种工作模式,而是一个PHP在FastCGI模式运行下的进程管理器,全称 ...

  3. alpha-咸鱼冲刺day8-紫仪

    总汇链接 一,合照 emmmmm.自然还是没有的. 二,项目燃尽图 三,项目进展 正在进行页面整合.然后还有注册跟登陆的功能完善-- 四,问题困难 数据流程大概是搞定了.不过语法不是很熟悉,然后还有各 ...

  4. 201621123040《Java程序设计》第七周学习总结

    1.本周学习总结 1.1思维导图:Java图形界面总结 2.书面作业 2.1GUI中的事件处理 2.1.1写出事件处理模型中最重要的几个关键词. 关键词:事件 事件源 事件监听器 2.1.2任意编写事 ...

  5. 20162328蔡文琛week06

    学号 2016-2017-2 <程序设计与数据结构>第X周学习总结 教材学习内容总结 继承是从已有类派生出一个新类的过程. 继承的目的之一之复用已有的软件. 继承呢在子类和父类见建立了is ...

  6. python 闭包计算移动均值及nonlocal的使用

    class Averager1(): '''计算移动平均值的类第一种写法''' def __init__(self): self.series = [] def __call__(self,new_v ...

  7. 用greenlet实现Python中的并发

    from greenlet import greenlet def test1(): print 12 gr2.switch() print 34 def test2(): print 56 gr1. ...

  8. Flask 蓝图(Blueprint)

    蓝图使用起来就像应用当中的子应用一样,可以有自己的模板,静态目录,有自己的视图函数和URL规则,蓝图之间互相不影响.但是它们又属于应用中,可以共享应用的配置.对于大型应用来说,我们可以通过添加蓝图来扩 ...

  9. LeetCode & Q53-Maximum Subarray-Easy & 动态规划思路分析

    Array DP Divide and Conquer Description: Find the contiguous subarray within an array (containing at ...

  10. Linq 延迟加载

    IList<Student> ssList = new List<Student>() { , StudentName = "John", } , , St ...