内存组成
内存区主要可以划分为5个区域,如上图。
java堆(Heap)
堆内存是运行时的数据区域。有新生代和旧生代组成,存储new对象的地方。这块是GC的主要区域。从存储的内容我们可以很容易知道,方法区和堆是被所有java线程共享的。
持久带PermGen space属于非堆内存,是指内存的永久保存区域。这块内存主要是被JVM存放Class和Meta信息的。Class在被Loader时就会被放到PermGen space中,它和存放类实例的堆内存区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很CLASS的话,就很可能出现PermGen space错误,这种错误常见在web服务器对JSP进行pre compile的时候。如果你的应用程序下用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么很可能就会产生PermGen space溢出的异常了。方法区(Method Area)
用于存储应用程序的所有类结构信息,虽然JVM规范把方法区描述为堆的一个逻辑部分,但它属于非堆内存。存储信息包括常量池、静态变量、构造函数等。它是在 Java 虚拟机启动时创建,由所有线程共享。JVM栈(Stack)
JVM栈总是和线程关联在一起,每当创建一个线程时,JVM就会为这个线程创建一个对应的java线程栈。
在这个java栈中又会包含多个栈帧,每运行一个方法就创建一个栈帧,每个栈帧对应着当前线程栈对每个方法的每次调用。方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程。所以java栈是每个线程私有的,线程生就生,线程灭就灭。栈中的栈帧随着方法的结束也会撤销,内存自然就跟着回收了。
栈帧又分为局部变量区和操作数栈两部分。局部变量区用于存放方法中的局部变量和参数;操作数栈用于存放方法执行过程中产生的中间结果;每个线程栈大小, JDK1.5+ 默认大小为 1M。一般来说如果栈不是很深的话, 1M 是绝对够用了的。程序计数器(PC Register)
用于保存当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证线程切换回来后,还能恢复到原先状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的。本地方法栈(Native Method Stack)
和java栈的作用差不多,只不过是为JVM使用到的native方法服务的。
回收算法和过程
当一个URL被访问时,内存申请过程 如下:
(1)JVM会试图为相关Java对象在Eden中初始化一块内存区域。
(2) 当Eden空间足够时,内存申请结束。否则到下一步。
(3)JVM试图释放在Eden中所有不活跃的对象(这属于1或更高级的垃圾回收), 释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区。
(4)Survivor区被用来作为Eden及OLD的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区。
(5)当OLD区空间不够时,JVM会在OLD区进行完全的垃圾收集(0级)。
(6)完全垃圾收集后,若Survivor及OLD区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现”out of memory错误。
友情链接:http://blog.youkuaiyun.com/tonytfjing/article/details/44278233