JVM的内存区域的溢出

2018-02-03 10:19:22来源:oschina作者:qq948939246人点击

分享
栈溢出

每个线程都拥有自己的栈,当前执行的方法在栈的顶部,每次方法调用时,一个新的栈帧创建并压到栈顶。当方法正常返回或抛出未捕获的异常时,栈帧就会出站。


栈可以是动态分配也可以固定大小。如果线程请求一个超过允许范围的空间,就会抛出一个StackOverflowError。如果线程需要一个新的栈帧,但是没有足够的内存可以分配,就会抛出一个 OutOfMemoryError。


栈溢出的场景
/**
* @author LXA
* 栈溢出
*/
public class Stack
{
public static void main(String[] args)
{
new Stack().test();
}
public void test()
{
test();
}
}

报错:


java.lang.StackOverflowError分析

当前调用的方法会在栈中创建栈帧,方法陷入死循环中,就会不停的在栈中创建对象,而不会销毁栈帧,直到栈没有空间时,就会溢出。


堆溢出:

类被初始化是,new出的对象,被分配在堆上,对象不停的创建,而且不被释放,在垃圾回收机制中,占满青年代,将会被移到老年代中,当老年代中的空间不够时,就会oom。


堆溢出的示例
/**
* @author LXA
* 堆溢出
*/
public class Heap
{
public static void main(String[] args)
{
ArrayList list=new ArrayList();
while(true)
{
list.add(new Heap());
}
}
}

报错:


报错:
java.lang.OutOfMemoryError: Java heap space
堆上的垃圾回收算法

基于 标记清除算法进行的。
新生代收集器使用的收集器:Serial、PraNew、Parallel Scavenge
老年代收集器使用的收集器:Serial Old、Parallel Old、CMS


四、GC的执行机制


由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。


Scavenge GC


一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。


Full GC


对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:


1.年老代(Tenured)被写满


2.持久代(Perm)被写满


3.System.gc()被显示调用


4.上一次GC之后Heap的各域分配策略动态变化


Java内存泄露

一般来说内存泄漏有两种情况。一种情况如在C/C++ 语言中的,在堆中的分配的内存,在没有将其释放掉的时候,就将所有能访问这块内存的方式都删掉(如指针重新赋值);另一种情况则是在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。第一种情况,在 Java 中已经由于垃圾回收机制的引入,得到了很好的解决。所以, Java 中的内存泄漏,主要指的是第二种情况。


Vector v = newVector( 10 );
for( inti = 1 ;i < 100 ; i ++ ){
Object o = newObject();
v.add(o);
o = null ;
}

在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 o 。在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 o 引用置空。问题是当 o 引用被置空后,如果发生 GC ,我们创建的 Object 对象是否能够被 GC 回收呢?答案是否定的。因为, GC 在跟踪代码栈中的引用时,会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管 o 引用已经被置空,但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。如果在此循环之后, Object 对象对程序已经没有任何作用,那么我们就认为此 Java 程序发生了内存泄漏。


持久代:

用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应
用可能动态生成或者调用一些class,例如Hibernate 等,在这种时候需要设置一个比较大的持
久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=进行设置。


持久带补充:持久带也称为方法区
方法区:方法区存储每一个java类的结构信息:比如运行时常量池,字段和方法数据,构造函数和普通方法的字节码内容以及类、实例、接口初始化时需要使用到的特殊方法等数据。
方法区也被称为永久代,如果不显示指定的话,GC回收的目标仅针对方法区的常量池和类型卸载


非堆内存的溢出

非堆内存包括:
永久代,包括:
方法区
驻留字符串(interned strings)
代码缓存(Code Cache):用于编译和存储那些被 JIT 编译器编译成原生代码的方法。


加载的类过多时,就会溢出

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台