Java内存区域 

 一、java运行时数据区域

  1、 程序计数器:程序计数器占据的内存空间较小,是当前运行线程执行的字节码的计数;分支、循环、跳转、异常处理、线程恢复等都要依赖技术器来对执行的字节码进行执行位置的计算来实现的。程序计数器的内存空间是每条线程独有的,也称之为“线程私有”的内存;计数器记录的是正在运行的字节码指令的地址,而如果是Native方法(本地方法),则计数器的值为空(Undefined)。此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。

  2、java虚拟机栈:java虚拟机栈也是线程私有的,它的生命周期与线程相同。每个方法在运行的时候都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法的调用,就对应着一个栈帧在虚拟机栈种从入栈到出栈的过程。局部变量表中存储了编译期可知的各种基本数据类型,(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型)和returnAddress类型。java虚拟机栈会抛出StackOverflowError异常和OutOfMemoryError异常。

  3、本地方法栈:本地方法栈是本地方法的“java虚拟机栈”。

  4、Java堆:Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例。Java堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”;根据Java虚拟机规范的规定,Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,当前主流的虚拟机都是按照可扩展实现的(当然可以是固定的),通过-Xmx和-Xms控制。当没有内存可以完成对象的实例的分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。

  5、方法区:方法区也是被线程共享的,它存储的是:被虚拟机加载的类信息、常量、静态变量、即使编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来。

对于Java堆从内存回收的角度来看,Java堆可以分为新生代和和老年代;很多人愿意把方法区称为“永久代”(限于HotSpot虚拟机);根据Java虚拟机规范的规定,当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

  6、运行时常量池:时方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。因为运行期间也可能会因为String类的intern()方法来将新的常量放入池中,所以当常量池无法再申请到内存时会抛出OutOfMemoryError异常。

  二、对象的创建

  1、 虚拟机遇到一条new的指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有就先进类的加载过程,在类加载完成后为新生对象分配内存。而分配内存的分配算法被受到收集器的算法的影响。在使用Serial、ParNew等带Compact过程(压缩)的收集器时,系统采用的分配算法是“指针碰撞”;而使用CMS这种基础MARK-Sweep算法(标记-清理算法)的收集器时,系统采用的分配算法是”空闲列表“;

  2、虚拟机为保证线程安全的两种内存分配机制:第一种是对分配内存空间的动作进行同步处理--实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性;另一种是采用本地线程分配缓冲(TLAB)。虚拟机在内存分配完成后把内存空间初始化为零值(不包括对象头),TLAB是分配的同时进行初始化。这样对象的初始值就不是空而是零,可以在不手动赋值的情况下进行调用。

  三、对象的内存布局

  1、对象在内存中存储的布局可以分为三个区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。

  2、对象头包括两部分:第一部分用于存储对象的运行时数据(如:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程的ID、偏向时间戳等);另一部分是类型指针,即指向其类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。java数组的记录长度的数据也会存在对象头中,虚拟机可以通过普通java对象的元数据信息确定java对象的大小。

  3、实例数据就是类中定义的各个字段的实例数据。

  4、对齐填充不是必然存在的,由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象示例数据部分没有对齐时,就需要通过对齐填充来补全。

  四、对象的访问定位

  1、访问对象有使用句柄和直接指针两种方式;都是通过存在Java虚拟机栈本地变量表中的reference指向Java堆来找到对象。

  2、使用句柄的方式:通过reference来访问Java堆中专门划分出来的句柄池(这时reference中存储的就是对象的句柄的地址),而句柄池中的句柄包含了到对象示例数据的指针和到对象类型数据的指针;存放对象实例对象的实例池在Java堆中,对象类型数据在方法区。这种方法的好处就是在对象移动时(垃圾收集时经常移动对象)只需要改变句柄中的指向对象实例数据的指针。

  3、使用直接指针访问,reference直接指向对象实例数据和到对象类型数据的指针。这种方式的好处就是更快,

深入理解Java虚拟机--阅读笔记一的更多相关文章

  1. 深入理解Java 虚拟机阅读笔记(一)

    1.程序计数器- 占用空间:较小 作用:字节码行号指示器 作用详情:指示指令执行,如(字节码的执行,分支,循环,跳转,异常处理,线程恢复) 特点:线程私有(每个计数器独立计算,上下文相互独立). 2. ...

  2. 深入理解Java虚拟机--阅读笔记二

    垃圾收集器与内存分配策略 一.判断对象是否已死 1.垃圾收集器在对堆进行回收前,要先判断对象是否已死.而判断的算法有引用计数算法和可达性分析算法: 2.引用计数算法是给对象添加引用计数器,有地方引用就 ...

  3. 深入理解JAVA虚拟机阅读笔记4——虚拟机类加载机制

    虚拟机把描述类的Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 在Java语言中,类型的加载.连接和初始化过程都是 ...

  4. 深入理解JAVA虚拟机阅读笔记1——JAVA内存区域

    一.Java内存区域 1.程序计数器 线程私有. 当前线程所执行的字节码的行号指示器.由于JAVA是多线程的,因此每个线程都独立的程序计数器. 异常:没有规定任何OutOfMemeryError情况的 ...

  5. 深入理解java虚拟机阅读笔记(1)运行时数据区域

    java虚拟机所管理的内存区域主要分为方法区.堆:虚拟机栈.本地方法栈.程序计数器,如图: 1.程序计数器是当前线程所执行的字节码行号指示器,用以记录当前指令执行的位置.程序计数器是线程私有的,每个线 ...

  6. 深入理解JAVA虚拟机阅读笔记6——线程安全与锁优化

    线程安全:如果一个对象可以安全的被多个线程同时使用,那它就是线程安全的. 一.Java中的线程安全 1.不可变 不可变的对象一定是线程安全的.String.枚举类型.java.lang.Number的 ...

  7. 深入理解JAVA虚拟机阅读笔记3——垃圾回收器

    一.垃圾收集器总览 新生代:Serial. ParNew. Parallel Scavenge 老年代:CMS.Serial Old. Parallel Old 最新的:G1 并行和并发的区别: 并行 ...

  8. 深入理解JAVA虚拟机阅读笔记2——垃圾回收

    线程私有的程序计数器.虚拟机栈和本地方法栈随线程而生,随线程而灭.栈中的栈帧随方法的进入和退出有条不紊的入栈和出栈. 而Java堆和方法区因为需要多大内存.创建多少对象都是不确定的,因此这两个区域是垃 ...

  9. 深入理解Java虚拟机--阅读笔记三

    垃圾收集器 手机算法是内存回收的方法论,垃圾收集器是内存回收的具体实现. 并行:指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态 并发:值用户线程与垃圾收集线程同时执行(但并不一定是并行的) ...

随机推荐

  1. Linux系统CentOS 7配置Spring Boot运行环境

    从阿里云新买的一台Linux服务器,用来部署SpringBoot应用,由于之前一直使用Debian版本,环境配置有所不同,也较为繁琐,本文主要介绍CentOS下配置SpringBoot环境的过程 新建 ...

  2. 关于错误 openssl/ssl.h:没有那个文件或目录的解决办法

    原文链接:https://blog.csdn.net/kulala082/article/details/68484314 首先得安装openssl:sudo apt-get install open ...

  3. Token的生成和检验

    package TestToken; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt. ...

  4. abstract抽象

    abstract:抽象 是用来修饰抽象类和抽象方法的 那么什么抽象,抽象有究竟有什么用呢?? 我们知道,“类”是某一类具有相同特征或行为的物事,是将这些物事特征向上抽取得来的:“父类”也是子类不断向上 ...

  5. python互斥锁

    互斥锁 进程之间数据隔离, 但是多个进程可以共享同一块数据,比如共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱,如下 from mu ...

  6. Numpy.random中shuffle与permutation的区别(转)

    huffle与permutation的区别 函数shuffle与permutation都是对原来的数组进行重新洗牌(即随机打乱原来的元素顺序):区别在于shuffle直接在原来的数组上进行操作,改变原 ...

  7. Java实现二分法排序

    二分法:(二分法不是只能做数组,这里的数组只是为了举例) 在给出的有序排列的数组中,把目标值和数组中间值进行比较,如果相等,则返回中间值下标,如果目标值小于中间值,就从数组的前半段再次执行二分法查找, ...

  8. php javascript comet

    简单描述: comet是用ajax实现的服务器推送,有两种实现comet的方式,长轮询和流,这里只实现长轮询. 长轮询的过程:页面发起一个服务器请求,然后服务器一直保持连接打开,直到有数据返回.返回数 ...

  9. 《CEO说 像企业家一样思考》读书笔记

    伟大的CEO和街头小贩一样都有共同的思维方式,他们总是能够通过复杂的表象看到商业本质,化繁为简,抓住企业经营的根本要素:商业智慧.所谓商业智慧,即企业家最应关注的企业运营的六个关键要素:现金净流入.利 ...

  10. Android在代码中获取应用签名

    平时都是用AS敲命令获取签名信息...还没有在代码中获取过签名~ 也算是老编程了,没做过这个稍微有点尴尬...本着有好轮子就用的原则,网上找了几篇博客,这块内容已经很完善了,我也没什么可以优化的... ...