1.内存区域划分

jvm在执行java程序过程中会将管理的内存划分成若干不同的数据区域,他们分别是程序计数器,堆,方法区,虚拟机栈,本地方法栈。

1.1指令计数器

指令计数器是线程私有的,每个线程都有独立的指令计数器,计数器记录着虚拟机正在执行的字节码的指令地址,分支,循环,跳转,异常处理和线程恢复等操作都依赖这个计数器完成,如果线程执行的native方法,则这个计数器为空。

1.2虚拟机栈

虚拟机栈是线程私有的,主要用于存放局部变量表,操作栈,动态链接,方法出口等信息,由于每个方法被执行都会创建对应的线帧,方法被调用到直至完成调用的过程,实际对应线帧在操作栈中入栈和出栈的过程。在java虚拟机规范中,对这个区域规定了两种异常情况:如果线程请求的栈深度大于规定的深度,则抛出StackOverFlowError异常;如果虚拟机栈的动态扩展到了无法申请的足够内存时候将抛出OutOfMemberError异常。

1.3本地方法栈

本地方法栈和虚拟栈的功能相似,包括上述2个异常情况也一样,区别在于虚拟机栈是为虚拟机执行的java服务,而本地方法栈是为虚拟机使用的Native方法服务。

1.4堆

堆是内存中最大的区域,并且它是所有线程共享的区域。它的唯一作用就是存放对象实例,根据jvm规范的规范,它的内存空间可以使不连续的,只要在逻辑上连续的即可。

为对象分配内存就是把一块大小确定的内存从堆内存中划分出来,通常有两种方法实现:

1 、指针碰撞法
     假设Java堆中内存时完整的,已分配的内存和空闲内存分别在不同的一侧,通过一个指针作为分界点,需要分配内存时,仅仅需要把指针往空闲的一端移动与对象大小相等的距离。

2、空闲列表法
     事实上,Java堆的内存并不是完整的,已分配的内存和空闲内存相互交错,JVM通过维护一个列表,记录可用的内存块信息,当分配操作发生时,从列表中找到一个足够大的内存块分配给对象实例,并更新列表上的记录。

对象创建是一个非常频繁的行为,进行堆内存分配时还需要考虑多线程并发问题,可能出现正在给对象A分配内存,指针或记录还未更新,对象B又同时分配到原来的内存,解决这个问题有两种方案:

a、采用CAS保证数据更新操作的原子性;

b、把内存分配的行为按照线程进行划分,在不同的空间中进行,每个线程在Java堆中预先分配一个内存块,称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB)

1.5 方法区

方法区和堆一样,是被所有线程共享的运行时区域,它用于存放被虚拟机加载的累信息,常量,静态变量,即时编译后的代码等数据,跟堆的情况一样,当方法区无法满足内存分配需求时,也会抛出OutOfMemberError的异常。

运行时常量池也属于方法区的一部分。class文件除了有版本,字段,方法,接口等描述信息外,其中还有信息是常量池,用于存放编译后的各种字面量和符号引用,这部分将在类加载后存放到方法区的常量池中。另外java语言并非要求常量一定一定在编译期间产生,即是并非预置入的class文件常量池的内存才能进入方法区的运行时常量池,运行期间同样也能进入。

1.6直接内存

直接内存并不属于虚拟机运行时的数据区的一部分, 也不是java虚拟机规范中定义的内存区域,但这部分内存被频繁使用到,并且也会爆outofmemoryError异常。

在java jdk1.4中加入了NIO类,引入了基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以直接使用Native函数分配堆外的内存,然后通过存储在java堆中DirectByteBuffer对象作为这块内存的引用直接操作,这样避免了java堆和Native堆来回复制的问题,提升了行性能。

1.7对象访问方式

java虚拟机规定了一个对象变量指向一个对象的引用,并没有定义这个引用以何种方式去定位,以及访问到java堆的具体位置,所以不同的虚拟机实现对象访问的方式略有不同,大概主流的分为:句柄和直接指针。

使用句柄访问方式,java堆会划出一个内存区域作为句柄池,对象的变量存储的就是句柄池的地址,而句柄池中就存放了对象实例的数据以及对象类型信息的地址信息。若使用直接访问方式,对象变量中存储的直接是对象实例的数据以及对象类型信息的地址信息。

两种访问方式各有优势,句柄访问的优势在于对象变量可存储稳定的地址,当对象移动时,只需改变句柄池的地址,变量本身无需修改。直接访问的优势明显在于访问速度快,sun HotSpot就是采用第二种对象访问方式。

1.8 对象的内存布局

对象在内存中布局可以分成三块区域:对象头、实例数据和对齐填充。

1、对象头
对象头包括两部分信息:运行时数据和类型指针,如果对象是一个数组,还需要一块用于记录数组长度的数据。

a、运行时数据包括哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向锁ID和偏向时间戳等,这部分数据在32位和64位虚拟机中的长度分别为32bit和64bit,官方称为"Mark Word"。Mark Word被设计成非固定的数据结构,以实现在有限空间内保存尽可能多的数据。
32位的虚拟机中,对象未被锁定的状态下,Mark Word的32bit中25bit存储对象的HashCode、4bit存储对象分代年龄、2bit存储锁标志位、1bit固定为0,具体如下:

其它状态(轻量级锁定、重量级锁定、GC锁定、可偏向锁)下Mark Word的存储内容如下:

b、对象头的类型指针指向该对象的类元数据,虚拟机通过这个指针可以确定该对象是哪个类的实例。

2、实例数据
实例数据就是在程序代码中所定义的各种类型的字段,包括从父类继承的,这部分的存储顺序会受到虚拟机分配策略和字段在源码中定义顺序的影响。

3、对齐填充
由于HotSpot的自动内存管理要求对象的起始地址必须是8字节的整数倍,即对象的大小必须是8字节的整数倍,对象头的数据正好是8的整数倍,所以当实例数据不够8字节整数倍时,需要通过对齐填充进行补全。

jvm的内存区域简介的更多相关文章

  1. JVM的内存区域划分以及垃圾回收机制详解

    在我们写Java代码时,大部分情况下是不用关心你New的对象是否被释放掉,或者什么时候被释放掉.因为JVM中有垃圾自动回收机制.在之前的博客中我们聊过Objective-C中的MRC(手动引用计数)以 ...

  2. JVM的内存区域划分

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

  3. 01 深入理解JVM的内存区域

    先来看看JVM运行时候的内存区域,如下图: 大多数 JVM 将内存区域划分为 Heap(堆).方法区.Stack(栈).本地方法栈.程序计数器.其中 Heap 和 方法区 是线程共享的,Stack.本 ...

  4. JVM的内存区域模型

    首先要明白一个概念,就是JVM的内存区域划分与java的内存区域模型是两个不同的概念,前者指的是在java中jvm会将一个程序划分为哪些块来存储对应的数据,后者是一个更宏观上的j概念,指的是java线 ...

  5. JVM的内存区域划分(转)

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

  6. 【java】JVM的内存区域划分

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

  7. 第1篇--基于jdk7和jdk8分析 JVM的内存区域

     基于jdk7和jdk8分析 JVM的内存区域 目录前言1.什么是JVM2.JRE/JDK/JVM是什么关系3.JVM执行程序的过程4. JVM的生命周期5.JVM垃圾回收一.运行时数据区的组成1.程 ...

  8. JVM(二)-内存区域之线程私有区

    概述: 对于从事C.C++开发的程序员来说,在内存管理领域,他们既是拥有最高权力的"皇帝",又是从事最基础工作的劳动人民--既拥有每个对象的"所有权", 又担负 ...

  9. 深入理解jvm之内存区域与内存溢出

    文章目录 1. Java内存区域与内存溢出异常 1.1. 运行时数据区域 1.1.1. 程序计数器 1.1.2. java虚拟机栈 1.1.3. 本地方法栈 1.1.4. Java堆(Java Hea ...

随机推荐

  1. Intellij idea workflow 工作流插件安装

    idea提供支持的工作插件名字叫actiBPM,可以在idea中在线安装,但往往会连接不成功安装失败,所以这里提供了硬盘安装的方式: 首先是要去官网下载actiBPM插件,下载地址: http://p ...

  2. hdu 4902 Nice boat 线段树

    题目链接 给n个数, 两种操作, 第一种是将区间内的数变成x, 第二种是将区间内大于x的数变为gcd(x, a[i]). 开三个数组, 一个记录区间最大值, 这样可以判断是否更新这一区间, 一个laz ...

  3. 自己动手实现getElementsByClassName

    看了一句话,我们都是搬运工,github的搬运工,下面这代码搬运来自各个地方,最后成型. var classCache = {}; function getElementsByClassName(cl ...

  4. C++读写文件流的相关介绍

    掌握文本文件读写的方法了解二进制文件的读写方法 C++文件流:fstream // 文件流ifstream  // 输入文件流ofstream  // 输出文件流 //创建一个文本文件并写入信息//同 ...

  5. nyist 202 红黑树(二叉树中序遍历)

    旋转对中序遍历没有影响,直接中序输出即可. #include <iostream> #include <cstdio> using namespace std; int n; ...

  6. Android企业级程序完全退出的解决方案【转】

    http://blog.csdn.net/wangjinyu501/article/details/8763552 问题描述 在平常开发的过程中可以发现,很多开发者对于程序的退出都没有去认真的解决.一 ...

  7. git将本地项目添加到github远程仓库

    1.首先准备好你的github 账号密码,注册就不说了.. 2.由于本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以要在本地生成一个私钥和一个密钥 命令: $ ssh-keygen ...

  8. c 统计字符串中字符出现的个数

    1.单纯用数组来解题 思路:从左往右循环,每次碰到一个字符就和左边的字符串比较,如果有相同的就右移,如果没有找到相同的就从这个位置向右统计个数并输出. #include<stdio.h> ...

  9. connot find one or more components. please reinstall the application

    正在用 Visual Studio 2013 写程序,程序一直执行正常. 此时,手动把注册表"HKEY_USERS"的当前用户的权限删除.再运行程序会提示:“是否继续并运行上次的成 ...

  10. iOS 纯代码适配iPhone6,6+

    链接地址:http://blog.csdn.net/codywangziham01/article/details/37658399 转自:http://www.maxiaoguo.com/cloth ...