您的位置:首页 > 编程语言 > Java开发

java内存区域

2015-11-22 17:48 281 查看

Java内存区域

  我写这个文章只是对学习的一些记录加上自己的理解,所以很简陋,详细可参考周志明的《深入理解Java虚拟机》或者《Java虚拟机规范(Java SE 7版)》的中译本。

运行时的数据区域

深入理解Java虚拟机中的图画得不是特别详细,所以我在网上找了稍微详细一点的!


针对这个图我会详细说明程序技术器,Java虚拟机栈,本地方法栈,Java堆,方法区,运行时常量池,还有一个直接内存(图上没有)。

程序技术器-唯一一个没有OOM的区域

  程序计数器(Program Counter Register),又叫PC寄存器。从图上来看这是线程私有的。如果执行的是java方法,保存的是当前线程正在执行的字节码指令地址;如果正在执行Native,这个计数器为空(Undefined)。

  由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。

  在程序中,分支,循环,跳转,异常处理,线程恢复都需要这个计数器。

Java虚拟机栈

  从图中看,java虚拟机栈也是线程私有的。

  栈帧:方法运行时的基础数据结构,对应当前每一个没调用的方法,方法被调用的时候就会被压入栈底,也就是说当前执行的任务在栈顶,这也就很好的反应了递归中为什么会出现内存溢出,当递归不结束的时候,或者太长的时候,线程请求的栈的深度就会大于当前虚拟机栈的深度,内存溢出(OOM).

  局部变量表:存储该栈帧对应方法中的局部变量(包括了方法中的申明的非静态变量以及函数形参)。对于八大基本数据类型的变量,直接存储。对于对象引用,存储的是引用。这里注意64位长度的long和double占用两个局部变量空间,其他只有一个。空间大小在编译时确定。

  操作数栈:就和大家学习栈中的表达式求值一样,在方法中所有的计算都是借助于操作数栈来完成的。

  当然除了这些比较重要的以外还有,指向运行时常量池的引用,方法返回地址(当一个方法执行完毕后,要返回之前调用它的地方,因此栈帧中必须保存一个方法返回地址)等等

本地方法栈

  本地方法栈和java虚拟机栈所发挥的作用是非常相似的。区别是java虚拟机栈为执行Java方法而服务,本地方法为Native方法服务。在JVM规范中,并没有对本地方发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

Java堆

  java堆和C,C++不同。在C中堆是可以由程序员管理的(申请和释放)。

  但在Java中就不行,程序员基本不用关心空间释放的问题,java的垃圾回收机制会自动处理。

  在java虚拟机规范中说:所有的对象实例以及数组都要在堆上分配。但随着JIT编译器的发展与逃逸分析技术逐渐成熟,发生了一些变化,其实也不是那么绝对了。堆被所有线程共享,JVM中只有一个堆。

方法区

  方法区和java堆一样,是每个线程共享的内存区域,它用于存储已被虚拟机加载的类信息,常量,静态变量等数据,别名叫Non-Heap(非堆),目的是把Java堆区分开。

  在JVM规范中,没有强制要求方法区必须实现垃圾回收。很多人习惯将方法区称为“永久代”,是因为HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。

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和1.7不同。在1.6中两个都是false,在1.6中,intern方法会把首次遇到的字符串实例复制到永久代中,StringBuilder中是在java堆上的,两个引用必然不同,将返回false。JDK1.7的结果一个是true,另一个是false。JDK1.7的intern()实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此str1是true,但是str2这里有个问题首次出现,“java”这个特殊字符串是在之前已经出现过他了,字符串常量池中已经有”java”这个引用,和str2指向堆的引用不同,返回false。

直接内存

  直接内存并不是虚拟机运行时数据去的一部分,也不是javav虚拟机规范中定义的内存区域。但是这部分内存也被频繁地使用,而且也可能导致OOM异常。

  在jdk1.4中新加入了NIO类,引入了一种基于通道和缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用来操作,避免了在java堆和Native堆来回复制数据

  直接内存不会受到java堆的大小限制,但是会受到机器的限制。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: