java JVM内存模型与内存溢出异常

1 前言

声明:本文参考了深入理解Java虚拟机:JVM高级特性与最佳实践 第2版,可作为该书的一个读书笔记

2 jvm 内存模型

java 虚拟机在运行时,内存模型如下:
这里写图片描述

可以看到,虚拟机栈,本地方法栈,程序计数器是线程私有的,随着线程的消亡而消亡,生命周期与线程相同。而方法区,堆的生命周期是和jvm(一个jvm实例就是一个进程)一起的,随着jvm的消亡而消亡。

下面分别介绍各个区域

程序计数器
程序计数器是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。因为java虚拟机的多线程是轮流切换并分配处理器资源的,因此每个线程都有一个独立的程序计数器。因此该区域不是内存共享的。
另外,如果执行的是Native方法,这个计数器的值为null,并且该区域是jvm中唯一一个不会发生OutOfMemoryError的区域

虚拟机栈
与程序计数器一样,虚拟机栈也是线程私有的。每个方法在执行的时候会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。方法的执行对应着方法栈的入栈和出栈过程。
这里写图片描述

本地方法栈
本地方法栈与虚拟机栈的作用很相似,区别是本地方法栈是为执行本地方法服务的。有的虚拟机如HotSpot直接将本地方法栈与虚拟机栈合二为一。
本地方法栈也会抛出与虚拟机栈一样的StackOverflowError和OutOfMemoryError异常

java堆
java堆是所有线程共享的一块内存区域,由虚拟机启动时创建,堆得唯一目的就是存储对象的实例。几乎所有的对象实例都在改内存区域分配(但是随着JIT的发展及逃逸分析技术的发展,导致发生了一些不一定)。
java堆是垃圾收集器管理的主要区域。根据现在GC技术采用的算法(分代收集算法),现在java堆一般分为新生代和老生代。
新生代:主要存放应用程序中生命周期短的内存对象,经常被回收
老生代:主要存放应用程序中生命周期长的内存对象

另外,从内存分配的角度来看,堆中可能划分多个线程私有的分配缓冲区(TLAB)。

另外,java堆可以是处于物理上不连续的内存空间,只要逻辑上连续即可。通过-Xmx,-Xms来控制堆的扩展。
如果堆无法扩展,将会抛出OutOfMemoryError异常

方法区
方法区也是多个线程共享的区域。它主要存储已被虚拟机加载的类信息(包括类中定义的方法),常量,静态变量,即时编译器优化后的代码等数据。

对于HotSpot虚拟机来说,有时方法区又被称为“永久代”,这是因为HotSpot选择把垃圾收集扩展至方法区。
当方法区无法再分配扩展内存时,会抛出OutOfMemoryError异常

另外,方法区中包含运行时常量池运,是方法区的一部分,主要存储编译器期间生成的各种字面量及符号引用。

直接内存
直接内存并不是虚拟机的一部分,也不是java虚拟机规范中定义的内存区域,但是这部分内存在java程序中也经常被用到。也有可能导致OutOfMemoryError异常的出现。

例如:java程序中通过NIO(New Input/Output),通过native函数直接分配的内存就属于直接内存。
直接内存并不受java堆大小的限制,只是受本机物理内存的限制。

3 jvm内存溢出异常

在时机开发中,对于现在的虚拟机,往往可以配置该虚拟机的各个部分内存的扩展,下面来介绍一下配置的参数以及对应的内存溢出。

虚拟机栈与本地方法栈
对于HotSpot来说,这二者是合二为一的。
-Xss:设定栈容量大小 -Xss100MB
一般栈有下面两种异常
1 线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常
2 如果虚拟机允许扩展,当无法申请到足够的内存时,将抛出OutOfMemoryError
一般在单线程中,当我们定义大量本地变量,或者减少栈内存容量,抛出的都是StackOverflowError异常,实验不出OutOfMemoryError异常
在多线程中,通过不断的建立线程倒是可以抛出OutOfMemoryError异常。
发生异常的关键字如下:

error: java.lang.StackOverflowError
[INFO]  at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5365)

java堆
-Xms:设置虚拟机堆的最小值 -Xms1000MB
-Xmx: 设置虚拟机堆的最大值 -Xmx1500MB
通过-Xmx,-Xms来控制堆的扩展。如果堆无法扩展,将会抛出OutOfMemoryError异常
一般提示信息有这样一句代码:

Java.lang.OutOfMemoryError: Java heap space 

方法区
使用-XX:permSize与-XX:MaxPermSize来限定方法区的大小
当方法区内存不足时,将会发生OutOfMemoryError异常
异常信息如下:

java.lang.OutOfMemoryError: PermGen space

直接内存溢出
-XX:MaxDirectMemorySize:指定直接内存大小,例如:-XX:MaxDirectMemorySize=100MB
当直接内存溢出时,Heap Dump文件不会看到明显的异常,如果发现OOM之后的dump文件很小,而程序中使用了NIO,就要考虑直接内存的溢出了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值