java 内存类型是_Java内存报错简单分类与分析

首先看下java的内存结构,请看下图

cf8a4ec51ef7fdaa9b8a07b3bb8e274c.png

在运行时的内存区域有5个部分,Method Area(方法区),Java stack(java 虚拟机栈),Native MethodStack(本地方法栈),Heap(堆),Program Counter Regster(程序计数器)。从图中看出方法区和堆用黄色标记,和其他三个区域的不同点就是,方法区和堆是线程共享的,所有的运行在jvm上的程序都能访问这两个区域,堆,方法区和虚拟机的生命周期一样,随着虚拟机的启动而存在,而栈和程序计数器是依赖用户线程的启动和结束而建立和销毁。

Program Counter Regster(程序计数器):

每一个用户线程对应一个程序计数器,用来指示当前线程所执行字节码的行号。由程序计数器给文字码解释器提供吓一条要执行的字节码的的位置。根据jvm规范,在这个区域中不会抛出OutOfMemoryError的内存异常。

Java stack(java 虚拟机栈):

这个区域是最容易出现内存异常的区域,每一个线程对应生成一个线程栈,线程每执行一个方法的时候,都会创建一个栈帧,用来存放方法的局部变量表,操作树栈,动态连接,方法入口,这和C#是不一样的,在C#CLR中没有栈帧的概念,都是在线程栈中通过压栈和出栈的方式进行数据的保存。jvm规范对这个区域定义了两种内存异常,OutOfMemoryError,StackOverflowError。

Native MethodStack(本地方法栈):

和虚拟机栈一样,不同的是处理的对象不一样,虚拟机栈处理java的字节码,而本地栈则是处理的Native方法。其他方面一致。

Heap(堆):

前面说了堆是所有线程都能访问的,随着虚拟机的启动而存在,这块区域很大,因为所有的线程都在这个区域保存实例化的对象,因为每一个类型中,每个接口实现类需要的内存不一样,一个方法内的多个分支需要的内存也不尽相同,我们只有在运行的时候才能知道要创建多少对象,需要分配多大的地址空间。GC关注的正是这样的部分内容,所以很多时候也将堆称为GC堆。堆中肯定不会抛出StackOverflowError类型的异常,所以只有OutOfMemoryError相关类型的异常。

Method Area(方法区):

用于存放已被虚拟机加载的类信息,常量,静态方法,即使编译后的代码。同样只能抛出OutOfMemoryError相关类型的异常。

程序:package test;

import java.util.ArrayList;

import java.util.List;

public class T {

private static byte[] bt = new byte[1024*1024];

public static void main(String[] args){

List list = new ArrayList();

while (true) {

list.add(bt);

}

}

}

报错:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

at java.util.Arrays.copyOf(Arrays.java:2760)

at java.util.Arrays.copyOf(Arrays.java:2734)

at java.util.ArrayList.ensureCapacity(ArrayList.java:167)

at java.util.ArrayList.add(ArrayList.java:351)

at test.T.main(T.java:8)

分析:

OutOfMemoryError说明是堆中出现的异常,heap space说明由于新建对象无法获得更多的内存而引发。

从实际开发角度来看,多数是因为代码编写BUG导致。当然不排除由于Jvm参数设置不当。

相关参数:

-Xms(堆的最小值),-Xmx(堆的最大值)

程序:package test;

public class T {

public static void main(String[] args){

Recursion recursion = new Recursion();

try {

recursion.recursionself();

} catch (Throwable e) {

System.out.println("current value :" + recursion.currentValue);

e.printStackTrace();

}

}

}

class Recursion {

public int currentValue = 0;

public void recursionself() {

currentValue += 1;

recursionself();

}

}

报错:current value :11433

Exception in thread "main" java.lang.StackOverflowError

at test.Recursion.recursionself(T.java:19)

at test.Recursion.recursionself(T.java:20)

at test.Recursion.recursionself(T.java:20)

分析:

栈内存容量设置过小,也可能是代码中线程或方法产生了死循环或者过长的递归调用。

单线程的环境下,无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机抛出StackOverflowError异常。

相关参数:

-Xss参数 栈内存容量大小

设置Tomcat相关参数后启动Tomcat

报错:Caused by: java.lang.OutOfMemoryError: PermGen space

分析:

程序加载的类过多时,而没有足够的空间。Web容器容易发生此类错误。

相关参数:

-XX:PermSize和-XX:MaxPermSize

程序:package test;

import java.lang.reflect.Field;

import sun.misc.Unsafe;

public class T {

private static final int _1MB = 1024 * 1024;

public static void main(String[] args) throws IllegalArgumentException,

IllegalAccessException {

Field unsafeField = Unsafe.class.getDeclaredFields()[0];

unsafeField.setAccessible(true);

Unsafe unsafe = (Unsafe) unsafeField.get(null);

while (true) {

unsafe.allocateMemory(_1MB);

}

}

}

报错:Exception in thread "main" java.lang.OutOfMemoryError

at sun.misc.Unsafe.allocateMemory(Native Method)

at test.T.main(T.java:12)

或:

java.lang.OutOfMemoryError: Direct buffer memory

分析:

如果你在直接或间接使用了ByteBuffer中的allocateDirect方法的时候,而不做clear的时候就会出现类似的问题。

常规的引用程序IO输出存在一个内核态与用户态的转换过程,也就是对应直接内存与非直接内存,如果常规的应用程序你要将一个文件的内容输出到客户端需要通过OS的直接内存转换拷贝到程序的非直接内存(也就是heap中),然后再输出到直接内存由操作系统发送出去,而直接内存就是由OS和应用程序共同管理的,而非直接内存可以直接由应用程序自己控制的内存,jvm垃圾回收不会回收掉直接内存这部分的内存,所以要注意。

Java虚拟机可以通过参数-XX:MaxDirectMemorySize设定本机直接内存可用大小,MaxDirectMemorySize没显式配置的时候,NIO direct memory可申请的空间的上限就是-Xmx减去一个survivor space的预留大小。

相关参数:

-XX:MaxDirectMemorySize

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值