java内存

来源:互联网 发布:linux设备驱动程序pdf 编辑:程序博客网 时间:2024/05/17 05:08

Java在执行程序时将管理的内存区域划分为不同的数据区域.包括方法区,虚拟机栈,本地方法栈,堆,程序计数器.



1.运行时数据区域

1.1. 程序计数器

   程序计数器是一块较小的内存空间.保存方法的执行信息,如果线程执行的是Java的方法,那么保存的是正在执行的虚拟机字节码指令(每一个线程一个程序计数器,互不干扰);
而如果执行的是native的方法,那程序计数器中的值为undefined.该内存区域是在jvm中唯一一个没有OutOfMemoryError的区域

1.2 虚拟机栈&本地方法栈

   虚拟机栈:与线程的生命周期相同.描述的是Java方法执行的内存模型:方法在执行的同时会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口信息。方法的执行过程对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
局部变量表存放的是已知的数据类型(boolean,byte,char,short,int,float,long,double)、对象引用类型。当进入一个方法时,方法在栈帧中需要多大的局部变量空间是完全确定的,不会在方法执行期间修改大小。
在方法执行期间空能会抛出两种异常:StackOverflowError、OutOfMemoryError。
StackOverflowError:当线程请求的栈深度大于虚拟机所允许的最大深度,抛出该异常。
OutOfMemoryError:若虚拟机栈可以动态扩展,在扩展时没有足够的内存,抛出该异常。
   本地方法栈:与虚拟机栈类似,只不过虚拟机栈是为Java方法服务,而本地方法则是为虚拟机执行native方法服务。

1.3 Java堆(所有线程共享)

   Java堆是虚拟机管理最大的一块内存区域,由虚拟机在启动时创建,唯一目的就是存放对象实例。即所有的对象实例以及数组都要在堆上分配(不是绝对的),也是GC主要服务的区域,故也称之为GC堆。
Java堆包括新生代|老年代|;Eden空间|from空间|To Survivor空间.
   堆的大小由-Xms(最小值)和-Xmx(最大值)控制,这样可以自动扩展堆的大小.堆的溢出:当GC Roots到对象之间有可达路径,那么当堆达到最大容量时将会抛出OutOfMemoryError.此时可以使用参数-XX:+HeadDumpOnOutOfMemoryError将内存快照打印出来.用于判断是出现内存泄漏(OutOfMemoryError)还是内存溢出(StackOverflowError).
   如果是内存泄漏,根据GC Roots找到是什么原因使得无法回收器无法回收对象.
   如果是内存溢出,应该检查内存是否还可以分配更大的空间.

1.4 方法区(线程共享Non-Heap)

   存放的是已经被虚拟机加载的类信息(类名,访问控制符),常量,静态变量,字段描述,方法描述等等,.该区域还可以选择不进行垃圾回收,
   运行时常量池:是方法区的一部分,存放编译器生成的各种字面量和符号引用,这部分内容在类加载后进入方法区的运行时常量池.
 直接内存:在NIO的应用中,可以使用Native函数库直接分配堆外内存(不属于Java堆的内存),通过存储在Java堆中的对象DirectByteBuffer作为这块内存的应用进行操作.(-XX:MaxDirectMemorySize控制直接内存的大小)
可以通过unsafe分配内存.
Field unsafeField = Unsafe.class.getDeclaredFields()[0];unsafeField.setAccessible(true);Unsafe unsafe = (Unsafe) unsafeField.get(null);while(true)unsafe.allocateMemory(_1M);
可能出现Access restriction错误,需要设置访问权限project->build path -> config...->JRE System Library ->Access rules -> edit ->access:**


可以通过-XX:PermSize 和-XX:MaxPermSize限制方法区的大小.String.intern():该方法会在遇到的第一个字符串常量时,将其放入到方法区中,而在下次遇到的时候直接访问对应的引用,而不会创建新的实例.
String str = new StringBuilder().append("java").append("script").toString();System.out.println(str.intern() == str); // trueSystem.out.println("javascript".intern() == str); // false
在jdk6中,会返回两个false,而在jdk7中,上一个true,下一个false;
jdk6:intern()方法会把首次遇到的字符串实例复制到方法区(永久代)中,而由StringBuilder创建的字符串实例是在Java堆上,两者明显不是同一个引用.
jdk7:intern()不会再把首次遇到的字符串实例进行复制到方法区,而是保存该字符串的引用,因此...



2.对象

当虚拟机在遇到一个new指令时,会检查该指令的参数是否在常量池中能定位到一个类的符号引用,并且是否被加载,解析,初始化过.如果没有,先进行类的加载,然后为其新生的对象分配内存空间.
空间的划分可以分为两种情况:
1.指针碰撞:所有使用过的和未使用过的界限分明,中间由指针作为分界点,那么在分配内存时,只需把指针挪动对应的大小即可.
2.空闲列表:内存分布并不均匀,使用的和未使用的均混杂在一起,此时就需要虚拟机维护一个列表,记录哪些被使用,哪些可以进行划分.
为了保证内存划分的正确性.可以预先为每一个线程留一块空间(本地线程分配缓冲Thread Local Allocation Buffer TLAB),哪个线程需要分配空间,就在其对应的内存空间上进行划分.当TLAB不够时,
需要进行同步分配.参数:-XX:+/-UseTLAB 是否使用TLAB.
在内存分配完成之后,虚拟机需要把内存空间都置为0,所以才会有实例字段会有初始值;然后虚拟机要对对象进行必要的设置,比如说该对象是哪个类的实例,如何才能找到类的元数据信息,对象的GC分代年龄.
然后紧接着执行<init>方法.
对象包括对象头,实例数据,对齐填充;
对象头:运行时数据:包括哈希码,GC分代年龄,锁状态标识等.
      类型指针: 由此可以知道该对象是哪个类的实例.引用的变量
实例数据:在程序中定义的各种字段信息.
对齐填充:
对象的访问: ①.使用句柄访问,即reference指向对象句柄,句柄中包含了对象实例数据与类型数据的具体地址信息
  ②.使用指针访问,reference存放的是对象的实际地址.(hotspot)可以节省一次寻址开销.


判断对象是否存活:
①.引用计数算法:只要有一个地方引用该对象,那么对象的计数器加1,当引用失效时,计数器减1,当计数器为0时,表明该对象已死,可以进行GC
②.可达性分析算法:对于一系列的GC Roots对象,当从这些对象向下进行搜索时,搜索的路径称为引用链,当一个对象没有任何引用链到达时,则证明该对象不可用,可以进行GC回收.
GC Roots:虚拟机栈中引用的对象;
 方法区中类静态属性引用的对象;
 方法区中常量引用的对象;
 本地方法栈中JNI(native方法)引用的对象.
当通过可达性分析时,即便没有任何的引用链到达,也不一定就是死亡,而是至少要经过两次标记过程:在经过GC Roots的引用链没有到达对象的,那么第一次被标记;并且还会进行一次筛选,筛选的条件是是否有必要执行finalize()方法.
当对象没有覆盖finalize()方法或者finalize()已经被执行过了,那么认为没有必要执行该方法了,此时对象可以回收;若有必要执行finalize()方法,那么该方法是最后一个进行自救的机会.若能够与GC Roots对象有对应的引用链,那么标记将被擦除.
 
方法区回收

方法区主要回收的是废弃常量和无用的类:
废弃常量:当没有任何地方对该常量进行引用,那么常量将被回收
无用的类:该类的所有实例对象均已被回收;
加载该类的classloader已经被回收
该类的java.lang.Class对象没有在任何地方被引用
使用 -verbose:class  + -XX:+ThaceClassLoading,-XX:+TraceUnClassLoading查看类的加载和卸载信息;

Java堆的回收

垃圾收集算法:
1.标记-清除算法:首先标记所有需要回收的对象,在标记完成后进行统一的回收;(在回收之后会产生大量的不连续的内存碎片,那么在分配较大的对象时,无法找到足够大的连续空间,不得不提前触发下一次GC动作.)


2.复制算法:将内存划分为两块一样大的空间每次只使用其中的一块,当其中一块使用完之后,将还存活的对象复制到另一块没有使用过的内存上,然后对已经使用过的进行全部清理.



该回收方法代价高,运行效率也高;多用于回收新生代,因为新生代多数是朝生夕灭,只不过分配比例不再是一半,而是划分为一块较大的Eden空间和两个较小的Servivor空间,每次使用eden和其中的一块servivor空间,
当回收的时候,将Eden中和servivor中存活的对象复制到另一块servivor上,然后进行全部的回收操作.












0 0
原创粉丝点击