Java 溢出讲解(堆、栈、方法区)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/baidu_28068985/article/details/83545822

一、内存溢出

内存溢出:通常出现在某一块内存空间块耗尽的时候。OutOfMemory,简称OOM。

1、堆溢出

原因:大量对象占据了堆空间,而这些对象都持有强引用,导致无法回收,当对象大小之和大于由Xmx参数指定的堆空间大小时,溢出错误就自然而然地发生了。

如:

package com.krycai;

import java.util.ArrayList;

public class Test1 {

public static void main(String[] args) {

    ArrayList<Byte[]> list = new ArrayList<Byte[]>();

    for(int i=0; i<1024;i++) {

        list.add(new Byte[1024*1024]);

    }

}

}

2、直接内存溢出

直接内存:通过Java代码,获得一块堆外的内存空间,这块空间是直接向操作系统申请的。直接内存的申请速度一般要比堆内存慢,但其访问速度要快于堆内存。

3、过多线程导致OOM

由于每一个线程的开启都要占用系统内存,因此当线程数量太多时,也有可能导致OOM。由于线程的栈空间也是在堆外分配的,因此和直接内存非常相似,如果想让系统直接支持更多的线程,那么应该使用一个较小的堆空间。

方法:

(1)、尝试减少堆空间。 -Xmx512m

(2)、减少每个线程所占的内存空间,使用-Xss参数可以指定线程的栈空间。

-Xmx1g -Xss128k

4、永久区溢出

永久区:是存放类元数据的区域,如果一个系统定义太多的类型,name永久区是有可能溢出的。

解决策略:

(1)、增加MaxPermSize的值。

(2)、减少系统需要的类的数量。

(3)、使用ClassLoader合理地装载各个类,并定期进行回收。

二、String

1、特点

不变性: 对象一旦生成,则不能再对它进行改变。可以提高多线程访问(只读)的性能。

针对常量池的优化:当两个String对象拥有相同的值时,它们只引用常量池中的同一个拷贝。当一个字符串反复出现时,这个技术可以大幅度节省内存空间。

类的final定义:在系统中不可能有任何子类,这是对系统安全性的保护。

2、String的内存泄露

由于疏忽或者错误造成程序未能释放已经不再使用的内存的情况,它并不是说物理内存消失了,而是指由于不再使用的对象占据内存不被释放,而导致可用内存不断减少,最终由可能导致内存溢出

三、虚拟机栈和本地方法栈溢出

关于虚拟机栈和本地方法栈,在java虚拟机规范中描述了两种异常:

(1)、线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常

(2)、虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

注:当栈空间无法继续分配时,到底是内存太小,还是已使用的栈空间太大,其本质上只是对同一件事情的两种描述而已。

四、方法区和运行时常量池溢出

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。对于这些区域的测试,基本的思路是运行时产生大量的类去填满方法区,直到溢出。

以上是简单的一些概念讲解,先有概念再去行动,针对性就比较强。

以上内容来源于 《实战Java虚拟机_JVM故障诊断与性能优化》《深入理解Java虚拟机 JVM高级特性与最佳实践》 的学习笔记,喜欢的点赞,谢谢。

猜你喜欢

转载自blog.csdn.net/baidu_28068985/article/details/83545822