初识java内存区域
目录:
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内存区域的更多相关文章
- 【转】Java内存管理:深入Java内存区域
转自:http://www.cnblogs.com/gw811/archive/2012/10/18/2730117.html 本文引用自:深入理解Java虚拟机的第2章内容 Java与C++之间有一 ...
- Java 内存区域和GC机制分析
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- Java虚拟机2:Java内存区域及对象
几个计算机的概念 为以后写文章考虑,也为巩固自己的知识和一些基本概念,这里要理清楚几个计算机中的概念. 1.计算机存储单位 从小到大依次为位Bit.字节Byte.千字节KB.兆M.千兆GB.TB,相邻 ...
- Java 内存区域和GC机制
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- Java系列笔记(3) - Java 内存区域和GC机制
目录 Java垃圾回收概况 Java内存区域 Java对象的访问方式 Java内存分配机制 Java GC机制 垃圾收集器 Java垃圾回收概况 Java GC(Garbage Collection, ...
- Java 内存区域划分
JVM的内存区域划分 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆.栈以及静态数据区.那么在Java语言当中,内存又是如何划分的 ...
- Java内存管理:深入Java内存区域
Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 对于从事C和C++程序开发的开发人员来说,在内存管理领域,他们既是拥有最高权力的皇帝 ...
- 2016021801 - Java内存区域学习笔记
根据<深入理解java虚拟机>学习归纳整理学习笔记 程序计数器 用途:当前线程的字节码文件的行号指示器.(当前机场负责控制飞机降落的空管员:当前线程表示当前机场, 所执行的字节码等同于被等 ...
- Java 内存区域和GC机制--备用
Java垃圾回收概况 Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代 ...
随机推荐
- Shell脚本 | 性能测试之启动时间
安卓应用的性能测试,通常包括六个指标:启动时间.内存.CPU.耗电量.流量.流畅度. 除了耗电量,其他五个指标的数据在我们团队中已经可以通过运行脚本的方式获取到. 今天给大家分享下启动时间的脚本吧- ...
- xml Schema include
first.xsd <?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs=&q ...
- php 冒泡排序的两种思路以及优化
php冒泡排序,两种思路,时间复杂度都是O(n^2),当然最优的时间复杂度就是O(n),以下说的都是正序排列(倒序的话,把内层循环的大于号换成小于号就好了) 第一种冒泡排序 思路就是把第一个数跟所有的 ...
- (转)Spring常用注解
使用注解来构造IOC容器 用注解来向Spring容器注册Bean.需要在applicationContext.xml中注册<context:component-scan base-package ...
- Oracle基础语句练习记录
1.往scott的emp表插入一条记录 insert into scott.emp(empno,ename,job) values(9527,'EAST','SALESMAN'); 2.scott的e ...
- 统计频率(map映照容器的使用)
问题描述 AOA非常喜欢阅读莎士比亚的诗,莎士比亚的诗中有种无形的魅力吸引着他!他认为莎士比亚的诗中之所以些的如此传神,应该是他的构词非常好!所以AOA想知道,在莎士比亚的书中,每个单词出现的频率各 ...
- Tomcat Connector原理
Tomcat工作原理 要了解其中的工作原理我们首先看如下两个图 Tomcat基本架构图: Tomcat请求示意图: 客户端的请求通过Connector接受处理后在到容器Engine->Host- ...
- Groovy中枚举简单使用--一对一映射
enum LevelEnum { S(1), C(2), B(3), A(4), X(5) private int value LevelEnum( int value) { this.value = ...
- 关于项目管理工具 maven
众所周知,maven是目前很常用的项目管理工具.一般情况下,通过在pom.xml添加相应内容,再maven-->update就会自动把相应的jar包下载.配置好,非常方便. 一般每新建一个wor ...
- JavaOne 2016主旨演讲畅谈Java近期及远期规划
在 JavaOne 2016 主题演讲开场,来自 Oracle 的 Java 产品管理负责人 Sharat Chander 指出 Java 盛行于个人和工作的日常生活各个领域,无论是大数据.物联网甚至 ...