Java虚拟机内存分配和溢出


前言

由于虚拟机自动内存管理机制的帮助下,不需要为每一个new出来的对象,去写delete/free代码。不容易出现内存泄漏和内存溢出的问题,

但是如果一旦出现问题,那将是非常可怕的 ,如果不了解虚拟机是怎么使用内存的,那么排查错误将会成为一项非常艰难的工资。

误区

经常有人讲java内存区分为堆内存和栈内存,这种分发比较粗糙,只能说明大多数程序员最密切的就是这两块

1、Java虚拟机内存分配

1.1、程序计数器(线程私有)

较小的内存空间,可以当做当前线程所执行字节码的行号指示器,每个线程都有自己的行号指示器,互补干扰,因此这部分内存区域也叫作私有区域

1.2、Java虚拟机栈(线程私有)

这里其实就是程序员通俗意义上的栈区,存放基本的数据类型和,对象的引用。(函数多的参数值,局部变量)



每次在方法执行的同时,都会创建一个栈帧,用来存储局部变量表,操作数栈(可以理解成数字),动态链接、方法出入口等信息。每一个方法执行的时候,就对应着一个栈帧在虚拟机栈中入栈和出栈的过程

1.1、这个区域出现的异常情况有两种

1、如果虚拟机请求栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError异常

2、如果虚拟机栈可以动态扩展(当前大部分虚拟机都可以)如果扩展时无法申请到足够的内存,将飘出OutOfMemoryError

1.3、本地方法栈

本地方法栈和虚拟机栈区别是,虚拟机栈为执行的java方法服务,而本地方法栈为Native方法服务。它也会抛出StackOverflowError和OutOfMemoryError异常

1.4、Java堆(线程共享)

Java堆是被所有线程共享的区域,在虚拟机启动时候创建, 此区域的唯一目标就是存放对象实例和数组,
Java堆是垃圾收集器管理的主要区域

1.5、方法区(线程共享)

方法去也是各个线程共享的内存区域,它用于存储以被虚拟机加载的类信息,常亮,静态常量,以及访问修饰符,字段描述,方法描述等,即时编译后的代码等数据。,也叫永久代

1.5.1、运行时常量池

用于存放编译器生成的各种字面量和符号引用

图解

1、所有的内存

WX20180409-110305@2x

2、实战 OutOfMemoryError异常

2.1、java堆溢出

2.1.1、测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
public class JavaHeap {
static class OOMObject{

}

public static void main(String[] args) {
List<OOMObject> oomObjects = new ArrayList<>();
while (true){
oomObjects.add(new OOMObject());
}
}
}

过一段时间之后报下面错误

1
2
3
4
5
6
7
8
9
10
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:265)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:239)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:231)
at java.util.ArrayList.add(ArrayList.java:462)
at com.hlj.jvm.memory.JavaHeap.main(JavaHeap.java:19)

Process finished with exit code 1

2.1.2、异常分析和解决

可以看到上面的提示Java heap space java堆内存


Author: jony
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source jony !
 Previous
垃圾收集器 垃圾收集器
前言垃圾收集器GC ,同通常需要考虑3个事情 1、那些内存需要回收 2、什么时候回收 3、如何回收 1、不需要回收的 程序计时器,虚拟机栈,本地方法栈这3个区域都是线程所私有的,随着线程而生,而死。 关于栈的话,基本上就是在运行方
2018-04-09
Next 
SpringBoot启动加载数据CommandLineRunner SpringBoot启动加载数据CommandLineRunner
前言  实际应用中,我们会有在项目服务启动的时候就去加载一些数据或做一些事情这样的需求。 
为了解决这样的问题,Spring Boot 为我们提供了一个方法,通过实现接口 CommandLineRunner 来实现。 order越小越先
2018-04-08
  TOC