目录:

  1.运行时数据区域

  2.对象的创建

  3.对象的内存布局

  4.对象的访问定位

一、运行时数据区域

  基本的java虚拟机运行时数据区如下图:

  

  下面我们就来逐个认识这几个运行时的数据区域

  1.程序计数器(Program Counter Register)

    它是一块比较小的内存,可以看做是当前线程执行的字节码行号指示器,每个线程都需要有一个独立的线程计数器,各线程间计数器互不影响,独立存储,因此也称此类内存区域为“线程私有”的内存

    情景:

    1)当线程正在执行一个java方法,则这个计数器记录的是正在执行的虚拟机字节码指令的地址

    2)正在执行的是Native方法,则这个计数器值为空

  2.Java虚拟机栈(Java Virtual Machine Stacks)

    此运行时数据区也是“线程私有”的,它的生命周期与线程相同。虚拟机栈描述的是Java执行的内存模型:每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至执行完成的过程就对应着一个栈帧在虚拟机中的入栈到出栈的过程。

    通常我们习惯将Java内存分为堆内存和占内存,这种分法比较粗糙,实际的内存区域远比这种复杂,而这里所说的栈就是我们的虚拟机栈

  3.本地方法栈(Native Method Stack)

    作用与虚拟机栈所发挥的作用相似,他们之间的区别不过在于虚拟机栈为虚拟机执行Java方法或字节码服务,而本地方法栈则为虚拟机使用到的Native方法服务

  4.Java堆(Java Heap)

    Java堆是被所有线程所共享的一块内存区域,在虚拟机启动时创建,目的就是用来存放对象实例,几乎所有的对象都是在这里分配(但随着技术发展,“所有”不是那么绝对了)

    Java堆是垃圾收集器的主要管理区域,也叫“GC”堆

    从内存回收的角度可以将Java堆分为“新生代”,“老年代”

  5.方法区(Method Area)

    也是各个线程共享的存储区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

  6.运行时常量池(Runtime Constant Pool)

    它是方法区的一部分,class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,存放编译期生成的各种字面量和符号引用,这部分内容会在类加载后进入方法区的运行时常量池中存放

  7.直接内存(Direct Memory)

    并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也会被频繁的使用

二、对象的创建

  1.在语言层面对象的创建仅仅是一个new关键字而已,而在虚拟机中对象的创建却很复杂

  2.当虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否被加载、解析、初始化过。如果没有,那必须先执行相应的类加载过程,在类加载完成后,接下来虚拟机将会为对象分配内存,对象所需的内存大小在类加载完成后就可完全确定(如何确定在后面的对象内存布局部分会提及),为对象分配空间的任务等同于把一块确定大小的内存从java堆中划分出来

  3.分配空间的方式主要有两种,根据Java堆是否规整来决定选择哪种分配方式,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定

    1)指针碰撞(Bump the Pointer):Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就是仅仅把指针向空闲空间那边挪动一段与对象大小相等的距离

    2)空闲列表(Free List):Java中内存不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单的进行指针碰撞了,虚拟机就必须要维护一个列表,记录那些快是可以使用的,再分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录

  4.内存分配完后,虚拟机需要将分配到的内存空间都初始化为0值(不包括对象头),如果使用了TLAB(本地线程分配缓冲Thread Local Allocation Buffer,每个线程在Java堆中预先分配的一小块内存),这一工作过程也可以提前至TLAB分配时进行。这一步骤保证了对象的实例字段在Java代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值

  5.接下来虚拟机要为对象进行必要的设置,如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息。这些信息存放在对象的对象头(Object Header)中。根据虚拟机当前的运行状态的不同,对象头会有不同的设置方式

  6.在上面步骤完成后,从虚拟机的角度来看对象已经创建完成,但从Java程序的视角来看,对象的创建才刚刚开始,初始化init还没有执行,执行new指令后接着执行init方法,把对象按照我们自己的意愿来初始化后,这样一个可用对象才算完全创建出来

三、对象的内存布局

  在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)

  1.对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。另一部分是类型指针,即对象指向他的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。并不是所有的虚拟机实现都必须在对象数据上保留类型指针,换句话说即是查找对象的元数据信息不一定要经过对象本身。此外,如果对象是一个Java数组,那在对象头中还必须有一块用来记录数组长度的数据,因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是数组的元数据中无法确定数组的大小

  2.实例数据部分,它是真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容

  3.对齐填充,不是必然存在的,它也没有特殊的含义,它的存在仅仅是为了起到占位的作用,因为HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须要是8字节的整数倍,而对象头部分正好是8字节的整数倍,因此,在对象实例数据部分没有对齐时,就需要通过对齐填充来补全

四、对象的访问定位

  1.建立对象是为了使用对象,我们的Java程序需要通过栈上的reference数据来操作堆上的具体对象,在Java虚拟机规范中只规定了一个指向对象的引用,并没有定义这个引用应该通过何种方式去定位、访问对中对象的具体位置,所以对象访问方式也是取决于虚拟机实现而定的。目前主流的访问方式有以下两种:

  

  

  1)如果使用句柄访问的话,那么Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据的具体地址信息

  2)如果使用直接指针访问,那么Java堆对象的布局中就必须考虑如何放置访问类数据类型的基本信息,而reference中存储的直接就是对象地址

  2.两种访问方式的比较:

  1)使用句柄的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而reference本身不需要修改

  2)使用直接访问的最大好处就是速度更快,它节省了一次指针定位的时间开销

补充:

  本博客是对《深入理解Java虚拟机》的学习总结,整理了第二章一些最为关键重要又易于理解的部分,在整理的同时也加深我对Java内存区域的理解,总之,希望本博客可以对大家理解Java的内存区域有所帮助

  您的支持是对博主学习总结深入思考的最大帮助

初识java内存区域的更多相关文章

  1. 【转】Java内存管理:深入Java内存区域

    转自:http://www.cnblogs.com/gw811/archive/2012/10/18/2730117.html 本文引用自:深入理解Java虚拟机的第2章内容 Java与C++之间有一 ...

  2. Java 内存区域和GC机制分析

    目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...

  3. Java虚拟机2:Java内存区域及对象

    几个计算机的概念 为以后写文章考虑,也为巩固自己的知识和一些基本概念,这里要理清楚几个计算机中的概念. 1.计算机存储单位 从小到大依次为位Bit.字节Byte.千字节KB.兆M.千兆GB.TB,相邻 ...

  4. Java 内存区域和GC机制

    目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...

  5. Java系列笔记(3) - Java 内存区域和GC机制

    目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...

  6. Java 内存区域划分

            JVM的内存区域划分 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内存又是如何划分的 ...

  7. Java内存管理:深入Java内存区域

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 对于从事C和C++程序开发的开发人员来说,在内存管理领域,他们既是拥有最高权力的皇帝 ...

  8. 2016021801 - Java内存区域学习笔记

    根据<深入理解java虚拟机>学习归纳整理学习笔记 程序计数器 用途:当前线程的字节码文件的行号指示器.(当前机场负责控制飞机降落的空管员:当前线程表示当前机场, 所执行的字节码等同于被等 ...

  9. Java 内存区域和GC机制--备用

    Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 ...

随机推荐

  1. hbase和zookeeper的安装和部署

    1.概要 1.1HBase的使用场景  大数据量 (100s TB级数据) 且有快速随机访问的需求.  例如淘宝的交易历史记录.数据量巨大无容置疑,面向普通用户的请求必然要即时响应.  容量的优 ...

  2. Supervisod的使用

    原文链接:http://blog.csdn.net/xyang81/article/details/51555473 Supervisor(http://supervisord.org/)是用Pyth ...

  3. new Thread与线程创建

    概要:new Thread 并不意味着已经创建了一个线程,只能说明创建一个类的对象实例而已.而真正创建线程的是start()方法,此方法将调用本地方法start0()创建本地线程,而Thread的ru ...

  4. 边界扫描(boundary scan)

    边界扫描(Boundary scan )是一项测试技术,是在传统的在线测试不在适应大规模,高集成电路测试的情况下而提出的,就是在IC设计的过程中在IC的内部逻辑和每个器件引脚间放置移位寄存器(shif ...

  5. 公众号开发之wx-tools+springboot应用实战-音乐爬虫推送[JAVA]

    springboot+wx-tools实践!音乐爬虫推送公众号DEMOGitHub地址:wx-tools 最终DEMO源码地址: music_collector 先理一下大概的开发步骤: 1. 创建一 ...

  6. AssemblyInfo.cs 详解

    前言 ? .net工程(包括Web和WinForm)的Properties文件夹下自动生成一个名为AssemblyInfo.cs的文件,一般情况下我们很少直接改动该文件.但我们实际上通过另一个形式操作 ...

  7. C# 将dll打包到程序中

    本文告诉大家如何把 dll 打包到程序中.很多时候的 软件 在运行的时候需要包括很多 dll 或其他的文件,这样的软件在给其他小伙伴,就需要做一个压缩包,或者用安装软件.这样感觉不太好,所以本文告诉大 ...

  8. MQ单一消息完整流程

    public class QueueManger { private static string QueuePath = @".\private$\{0}"; /// <su ...

  9. SQL SERVER 查看SQL语句IO,时间,索引消耗

    1.查看SQL语句IO消耗 set statistics io on     select * from dbo.jx_order where order_time>'2011-04-12 12 ...

  10. IDE MAC下的快捷键,自定义代码块。

    快捷键 参考博客:http://blog.csdn.net/iguiyi/article/details/51853728 alt+f7查找在哪里使用 相当于eclipse的ctrl+shift+G ...