对象的创建
虚拟机遇到一条字节码new指令时,开始对象创建过程。
- 首先去检查这个指令的参数是否能在常量池定位到一个类的符号引用;
- 检查这个符号引用代表的类是否已被加载、解析和初始化,如果没有就必须执行相应的类加载过程;
- 根据方法区中该类的信息确定对象的所需空间大小;
- 虚拟机为新生对象分配内存;
- 将对象实例的内存(不包括对象头)进行初始化为零值;
- 配置对象头的信息;
- 调用对象的构造函数进行初始化。
这样,一个真正可用的对象被完全构造出来了。
多线程中,引用指向对象的内存空间和对象初始化操作可能会出现重排序,这样会导致对象没有初始化就被其他线程使用了,就会出错。方法这种情况,就需要将对象声明为volatile。《Java并发编程艺术》
分配内存方法
虚拟机为新生对象分配内存有两种方法:
- 碰撞指针:如果虚拟机垃圾收集器采用的复制算法或者标记-整理算法,那么堆中空闲内存和已使用过的内存是连续放在一块的,二者中间存在一个指针作为分界点的指示器。这样分配内存的时候,只需要将指针向空间区域挪动一段与对象大小相同的距离,这种方式就是指针碰撞。
- 空闲列表 :如果虚拟机垃圾收集器采用的标记-清除算法:那么堆中的空闲内存和已使用内存不是连续的,是交错的,虚拟机通过维护一张列表来记录可用内存块,在分配内存的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表,这种分配方式就是空闲列表。
所以,虚拟机采用何种内存分配方法,取决于其所用的垃圾回收算法。
并发时对象分配
解决并发时对象分配的问题:即使仅仅修改一个指针所指向的位置,在并发情况下也并不是线程安全的,可能出现正在给对象A分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。
两种方案可以解决:
- 通过CAS配上失败重试的方法可以保证操作的原子性。
- 通过给每个线程一个本地线程分配缓冲区,当线程需要分配内存时,就在它相应的缓冲区分配,当缓冲区耗尽,就进行同步锁定。
对象的内存模型
对象在堆内存中可以划分为三个部分:
- 对象头
- 实例数据
- 对齐补充
1.对象头
对象头包括两类信息:
- 第一类用于存储对象自身的运行时数据:例如哈希码、GC分代年龄、锁状态、线程持有的锁、偏向线程ID、偏向时间戳等。
- 可能还存在第二类是类型指针,就是对象指向它的类型元数据指针,虚拟机通过这个指针来确定该对象是哪个类的实例。
- 如果是Java数组对象,对象头重还必须有一块数据用于记录数组的长度。
2.实例数据
是对象真正存储的有效信息,就是在程序代码里面定义的给中类型的字段内容,成员变量的值,其中包含父类的成员变量和当前类定义的变量。
3.对齐填充
作用只是占位符。并不是必然存在的。
HotSpot要求对象的起始地址必须是8子节的整数倍,所以任何对象的大小都必须是8子节的整数倍。对象头已经被设计为8字节的整数倍,如果实例数据没有对齐,就需要通过对齐填充部分进行补全。
对象访问定位
Java程序会通过栈上的reference数据来操作堆上的具体对象。所谓reference类型就是一个指向对象的引用。
主流的访问方式有两种:
- 使用句柄访问:
堆中划分出一块内存作为句柄池,用于存放对象实例数据和类型数据各自的地址信息。Java栈中存放的引用指向句柄池中对象的句柄地址。 - 直接指针访问:
引用类型指向的是对象的地址,不需要句柄池直接访问对象。但是对象的内存中就需要放置访问类型数据的相关地址信息。
比较:
- 句柄访问的好处是句柄地址稳定,当对象被垃圾收集器移动时,只会改变句柄池中的实例数据地址,而本地变量表中的引用不需要被修改。
- 直接指针访问的好处就是速度快,节省指针定位的时间开销。HotSpot主要采用这种方式进行访问。
深入理解Java虚拟机(二)——HotSpot对象创建、内存、访问的更多相关文章
-
深入理解Java虚拟机二:垃圾收集与内存分配
垃圾收集:垃圾收集要完成三件事,包括哪些内存需要回收,什么时候回收及如何回收. 1.需要回收的内存判定:没有引用指向原先分配给某个对象的内存时,则该内存是需要回收的垃圾 Java垃圾收集器在对内存进行 ...
-
深入理解Java虚拟机二 阅读笔记
xl_echo编辑整理.欢迎添加echo微信(微信号:t2421499075)交流学习. 百战不败,依不自称常胜,百败不颓,依能奋力前行.--这才是真正的堪称强大!! --- > 以下内容摘抄自 ...
-
深入理解Java虚拟机:垃圾收集器与内存分配策略
目录 3.2 对象已死吗 判断一个对象是否可被回收 引用类型 finalize() 回收方法区 3.3. 垃圾收集算法 1.Mark-Sweep(标记-清除)算法 2.Copying(复制)算法 3. ...
-
《深入理解Java虚拟机》学习笔记之内存分配
JVM在执行Java程序的过程中会把它所管理的内存划分若干个不同的数据区域,如下图: 大致可以分为两类:线程私有区域和线程共享区域. 线程私有区域 程序计数器(Program Counter Regi ...
-
《深入理解java虚拟机》读书笔记1--java内存区域
Java内存管理 本文主要介绍Java虚拟机运行时的内存区域是如何划分的.Java对象的创建过程.Java对象的内存布局.Java对象的访问定位 一:运行时区域划分 主要可以分为以下 几个: 程序计数 ...
-
java虚拟机(三)--HotSpot 对象
普通对象的创建(不包括数组和class对象): 当虚拟机遇到new指令时,会在常量池中检查是否包含这个类的符号引用(全限定名),通过这个确定是否经过类加载的过程,如果true,为该 对象分配内存,对象 ...
-
深入理解Java虚拟机(四)——HotSpot垃圾收集器详解
垃圾收集器 新生代收集器 1.Serial收集器 特点: 单线程工作,收集的时候就会停止其他所有工作线程,用户不可知不可控,会使得用户界面出现停顿. 简单高效,是所有收集器中额外内存消耗最少的. 没有 ...
-
深入理解Java虚拟机二之Java内存区域与内存溢出异常
运行时数据区域 1.线程独有的内存区域 PROGRAM COUNTER REGISTER 程序计数器 程序计数器空间较小,是当前线程执行字节码的行号指示器,字节码解释器工作时就是通过改变这个计数器的值 ...
-
深入理解Java虚拟机笔记——垃圾收集器与内存分配策略
目录 判断对象是否死亡 引用计数器算法 可达性分析算法 各种引用 回收方法区 垃圾收集算法 标记-清除算法 复制算法 标记-整理算法 分代收集算法 HotSpot算法实现 枚举根节点 GC停顿(Sto ...
随机推荐
-
qq邮箱邮我组件
http://openmail.qq.com/cgi-bin/qm_help_mailme?sid=uvkgSu7e0aOrc0Qc&t=open_mailme 邮我 使用"邮我&q ...
-
Nim教程【一】
这应该是国内第一个关于Nim入门的系列教程 什么是Nim 我们先来引述网友 Luikore的一段话: Nim 不是函数式的, 但 Nim 支持卫生宏, 可以做 AST 重写, 可以自定编译规则, 是静 ...
-
Xamarin.Android开发实践(十四)
Xamarin.Android之ListView和Adapter 一.前言 如今不管任何应用都能够看到列表的存在,而本章我们将学习如何使用Xamarin去实现它,以及如何使用适配器和自定义适配器(本文 ...
-
Android Fragment(碎片)的使用
简介 在Android中Fragment为一种可以嵌入活动中的UI片段.能让程序更加合理地利用大屏幕的空间. 使用方法 1.我们首先新建的一个onefragment.xml文件. <?xml v ...
-
expect实现scp/ssh-copy-id非交互
expect工具可以实现自动应答,从而达到非交互的目的. expect具体使用用法比较复杂,中文手册我正在翻译中,以后翻译完了做了整理再补.本文只有几个ssh相关最可能用上的示例. yum -y in ...
-
MYSQL——数据库存储引擎!
本人安装mysql版本为:mysql Ver 14.14 Distrib 5.7.18, for Win64 (x86_64),查看mysql的版本号方式:cmd-->mysql --vers ...
-
容器、应用服务器和web服务器的区别
什么是容器 servlet没main()方法.它们受控于另一个java应用,这个java应用称为容器(Container)Tomcat是这样的容器,Web服务器应用(Apache)得到一个指向serv ...
-
POJ1511来回最短路
POJ1511 问你从1到其它点得所有最短路之和 与 其他点到1得所有最短路之和 得总和 思路很明确就是两次最短路,翻转一次地图就好了 一开始就是两次spfa之间处理好数据得更新管理就好 vect ...
-
influxdb 的安装(centos)
安装命令: # for 64-bit systems wget http://s3.amazonaws.com/influxdb/influxdb-latest-1.x86_64.rpm sudo r ...
-
【最大流】【Dinic】bzoj1711 [Usaco2007 Open]Dingin吃饭
把牛拆点,互相连1的边. 把牛的食物向牛连边,把牛向牛的饮料连边. 把源点向牛的食物连边,把牛的饮料向汇点连边. 要把牛放在中间,否则会造成一头牛吃了自己的食物后又去喝别的牛的饮料的情况. #incl ...