Java虚拟机在执行java程序的过程中会把他管理的内存划分为若干个不同的数据区域各自用途、创建以及销毁时间各不相同。有的随着虚拟机进行的启动而存在,有的区域依赖于线程的启动和结束而建立以及销毁。如图:

1.程序计数器

  Jvm将这个计数看作当前线程(意味着只能支持单线程)执行某条字节码的行号指示器,会根据计数器的值来选取下一条需要执行的字节码指令。这个属于线程私有,不可共享,如果共享会导致计数混乱,无法准确的执行当前线程需要执行的语句。

  如果一个线程正在执行java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是Native方法,这个计数器的值为空(Undefined)。

该区域是唯一一个在java虚拟机规范中没有规定任何OutOfMemoryError的情况,因为只需要表示计数器的值,这个值大小是恒定的。

注:一个Native Method就是一个Java调用非Java代码的接口。一个Native Method是这样一个Java的方法:该方法的实现由非Java语言实现,比如C或C++。

2.Java虚拟机栈

栈内存就是指虚拟机栈。Java中每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

虚拟机栈表述的是Java方法执行的内存模型:每个方法在执行的同时,都会创建一个栈帧(Stack Frame),用于存放局部变量表、操作数栈、动态链接、方法出口等。口头所说的“栈内存”就是虚拟机栈中局部变量表部分,局部变量表存放了编译器可知的各种基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,不同于对象本身,可能指向一个对象起始地址的引用指针,也可能指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。

其中64位长度的long和double类型的数据会占用两个局部变量空间,其余数据类型只有一个。局部变量表所需的内存空间在编译期间完成分配。

如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

3.本地方法栈

虚拟机栈用来执行java方法,而本地方法栈用来执行本地方法。

   抛出异常的情况和虚拟机栈一样会抛出StackOverflowError异常和OutOfMemoryError异常。

4.Java堆

是jvm中内存最大、线程共享的一块区域。所有线程共享的一块内存区域,在虚拟机启动时创建。唯一的目的是存储对象实例。这里也是垃圾收集器主要收集的区域(GC堆:GarbageCollected Heap)。由于现代垃圾收集器都采用的是分代收集算法,所以java堆也分为新生代和老年代。

  线程共享的Java堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB),与存放内容无关,都存的是对象实例。

  Java堆可以处于物理上不连续的内存空间中,只要逻辑上连续即可。可以通过参数-Xmx(jvm最大可用内存)和-Xms(jvm初始内存)来调整堆内存,如果在堆中没有内存完成实例分配,并且堆也扩大至无法继续扩展时,会出现OutOfMemoryError的错误。    

5.方法区("永久代")

  Jvm中内存共享的一片区域,用来存储类信息、常量、静态变量、class文件。垃圾收集器也会对这部分区域进行回收,比如常量池的清理和类型的卸载,但是效果不理想。其  实现想像堆一样-XX:MaxPermSize设定管理这部分内存,而不用专门为该区进行内存管理编写代码。但是容易造成内存溢出,有逐步改为Native Memory来实现方法区的规划了,JDK1.7中已经把原本放在永久代的字符串常量池移出。
     方法区内存不够用的时候,也会抛出OutOfMemoryError错误。

6.运行时常量池

  运行时常量池是方法区的一部分(Runtime Constant Pool)。Class文件除了有类的版本、字段、方法、接口等信息,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这些内容在类加载后进入方法区的运行时常量池存放。

  Java虚拟机堆Class文件有严格规定,但是对运行时常量池没有,一般来说除了保存Class文件中描述的符号引用外,还会把翻译出来的直接引用也存储在运行时常量池中。运行期间也可将新的常量放入池中,如String类的inter()方法。

   代码示例:

public class Test {

    public static void main(String[] args){
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1); String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == str2);
}
}
在jdk1.6中,代码输出结果为false,false;在jdk1.7中,代码输出结果为true,false。

为什么会出现不一样的结果呢?

  因为在jdk1.6中,intern()方法会把首次遇到的字符串实例复制到方法区中,然后返回方法区中这个字符串实例的引用,而StringBuilder创建的字符串实例在堆上,
因此两者返回的肯定不是同一个引用,所以两个数据结果都为false。   而在jdk1.7中,intern()方法不再复制实例,只是在常量池中记录首次出现的实例的引用,因此如果该字符串是首次出现的,那么intern()返回的结果
和StringBuilder创建的是同一实例。所以第一个输出的结果为true。但是因为“java”这个字符串在执行StringBuilder.toString()以前已经出现过了,
常量池中存的不是str2实例的引用,不符合“首次出现”原则,因此返回的结果不是指向同一个实例,即输出为false。

  属于方法区,自然也受方法区限制,当常量池无法再申请到内存时会抛出OutOfMemoryError错误。

7.直接内存

  直接内存(Direct Memory)不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的数据区域。

  在JDK1.4中加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,可以使用Native函数库直接分配堆外内存,通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。在一些场景中避免在Java堆和Native堆中来回复制数据。

  本级直接内存不会受Java堆大小限制,受本机总内存大小以及处理器寻址空间限制,动态扩展时超过物理内存限制会出现OutOfMemoryError异常。

总体概括如图:

1.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开发者,一般不需要专门编写内存回收和垃圾清理代 ...

  10. JAVA内存区域和GC机制

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

随机推荐

  1. Sea.js学习4——Sea.js的配置

    可以对 Sea.js 进行配置,让模块编写.开发调试更方便. seajs.config seajs.config(options) 用来进行配置的方法. seajs.config({ // 别名配置 ...

  2. python 使用openpyxl来写数据到excel表格

    使用openpyxl写execl确实很方便.我先介绍用到的相关模块与函数 Workbook:工作簿模块,在内存创建一个工作簿. ExcelWriter:使用它向exel中写数据. get_column ...

  3. centos中开机时如何自启动samba服务器

    解决办法如下:1.编辑rc.local文件#vi /etc/rc.d/rc.local2.加入如下启动命令/usr/sbin/apachectl start/etc/rc.d/init.d/mysql ...

  4. AttributeTargets 枚举

    AttributeUsage AttributeTargets 在C#的类中,有的类加上了[AttributeUsage(AttributeTargets.Property)]这个是起什么作用的呢?A ...

  5. FC 坦克大战 老巢铁墙

    老巢外围铁墙E2A9:AC 80 EFEF80:A5 10 85 45 A5 45 AC D2 E2 用十六进制编辑器打开坦克大战的游戏文件搜索A5 45 F0 25 A5 0B改为AC 80 EF ...

  6. js 生成 yyyy-mm-dd 格式的逼格姿势

    关于 js 生成 yyyy-mm-dd 格式,往往都会采取手动拼接,一般不愿意为了小功能而去动用 momentjs 之类的插件. ps: 只分享简单方法,网上有 N 多 dateformat 代码,这 ...

  7. EXT 省市三级联动及默认选择

    var provinceStore = Ext.create('Ext.data.Store', { fields: ['id', 'name'], proxy: { type: 'ajax', ur ...

  8. 32. Path Sum && Path Sum II

    Path Sum OJ: https://oj.leetcode.com/problems/path-sum/ Given a binary tree and a sum, determine if ...

  9. 方法的重载(overload)和重写(override)的区别

    方法的重写Overriding和重载Overloading是Java多态性的不同表现.重写Overriding是父类与子类之间多态性的一种表现,重载Overloading是一个类中多态性的一种表现.如 ...

  10. 【knowledgebase】不要在一个很大的RDD上调用collect

    如果一个RDD很大以至于它的所有元素并不能在driver端机器的内存中存放下,请不要进行如下调用: val values = myVeryLargeRDD.collect()   collect将尝试 ...