jvm学习——java内存区域与内存溢出异常——丑九怪

本文详细解析JVM运行时数据区,包括程序计数器、虚拟机栈、本地方法栈、Java堆、方法区及运行时常量池,探讨各区域的作用、异常情况及其触发条件。

写在前面

对于java语言来说,相对于c、c++最大的区别可能就是在内存方面了,c、c++直接将内存交给了编程者,让其对所用的每字节空间“负全责”,而jvm提供了一整套自己管理自己的机制,虽然方便了编程者,但同时造成了隐患,如果编程者无法从内存角度清楚地认识到整个程序的执行过程,那么就有可能导致很多看起来无厘头的错误,在查错时也让人头疼无比。因此学习jvm是很有必要的

运行时数据区

  • jvm在执行java程序的过程中会把他所管理的内存划分为若干个不同的区域,以下将一一说明

1、程序计数器

  • 简单来说,可以将它理解为一个指针,指向当前线程所执行字节码的“地址”,通过改变他的值达到程序运行的效果,分支、循环、跳转、异常处理、线程恢复等都通过这个“指针”实现。每个线程都会有自己的程序计数器来控制程序的执行

2、java虚拟机栈

  • 当一个线程被建立时,就被分配了对应的虚拟机栈,java虚拟机栈用来描述java方法执行的内存模型,存储的基本的单位是栈帧,栈帧里存储每个方法调用时的具体信息。每个方法的调用和返回,对应栈帧的出栈和入栈操作
    (局部变量表、操作数栈、动态链接和方法出口等信息)。

  • java虚拟机栈有两个可能出现的异常,StackOverflowError和OutOfMemoryError
    这两个异常可以简单理解为:StackOverflowError是由于线程请求的栈深度大于虚拟机所允许的栈深度(过多的栈帧入栈)。如果Java虚拟机栈的大小可以动态扩展,当申请不到足够内存给栈时就会报OutOfMemoryError异常。(目前主流的HotSpot虚拟机不可以动态扩展,所以也就不会报这异常)

下面的代码演示了StackOverflowError,这是一个方法的递归调用,循环是死循环,一直在递归调用,程序执行之后,很快会出现异常

public static void main(String[] args) {
        method();
}
public static void method() {
    while (true)
    {
        method();
    }
}

在这里插入图片描述

2.1、本地方法栈
  • 与java虚拟机栈相似,本地方发栈为java程序中的native方法服务。个别虚拟机不区分java虚拟机栈和本地方发栈。

3、java堆

  • 这部分空间是所有线程共享的空间,主要存放对象实例,在虚拟机启动时创建。另外,这部分空间是垃圾回收器所管理的主要区域。java虚拟机规范规定,java堆只要逻辑上是连续空间即可。空间大小可以是固定的,也可以是动态可扩展的。因为其空间大小可能存在限制,所以这里也可能出现OutOfMemoryError,
List<Test1> strList = new ArrayList<>();
        while (true)
        {
            strList.add(new Test1());
        }

像这样不停地给创建的list中填充Test1的对象,因为每个对象都被list保存了下来,故不会被垃圾回收机制所回收,当创建的对象足够多,占满了所有的java堆空间,导致java堆开始扩展,当java堆扩展过程中再无可用空间的同时,报出OutOfMemoryError异常
在这里插入图片描述

4、方法区

  • 方法区是所有线程共享的区域,用于存储已经被虚拟机加载的类信息、常量、静态变量等信息。java规范将方法区从逻辑上划分为java堆的一部分。有些java虚拟机将方法区划分为java堆的一部分,将其规定为“永久代”,避免被GC回收。
  • 方法区无法满足内存分配需求时,也会出现OutOfMemoryError异常。尝试使方法区出现异常,比较简单的一种方法就是不停的创建类,并对其进行加载操作,想到使用动态代理就可以复现这个异常。
4.1、运行时常量池
  • 作为方法区的一部分。Class文件中的常量池,用于存储编译期生成的各种字面量和符号引用,这一部分内容在类加载之后被存放入方法区的运行时常量池。
  • 作为方法区的一部分,运行时常量池也有可能因为无法申请到足够内存而抛出OutOfMemoryError异常。

总结

  • 对于上述的jvm内存分布,在我的理解中分为两部分:线程共享区域线程独占区域。线程共享区域有java堆(存放对象),方法区(存放被加载的类的信息)。线程独占区域(每个线程都有一个的部分,互不干扰)有:程序计数器(指向当前指令的地址),java虚拟机栈(存放当前所执行对的方法),本地方法栈(存放当前所执行对的Native方法)。这些区域随着java的发展,有可能出现改变,但基本大同小异,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值