JVM运行时数据区、常见jvm异常例子

本文详细介绍了Java虚拟机(JVM)的内存结构,包括堆、Java虚拟机栈、方法区、程序计数器和本地方法栈等内容。阐述了各部分的作用、存储内容以及常见的内存溢出异常。

JVM 运行时数据区:

  • Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分为若干个不同的数据区域。

堆 (heap)

  • 线程共享
  • 存储:绝大部分创建的实例对象数组字符串常量池
  • 说明:
    – 对象引用存储在 ,指向存储着对象在堆中的地址
    – 堆内存中的对象存储着自己的成员变量,并不保存对象的方法,方法被保存在帧栈中。
  • 参数: -Xms1G - 初始堆内存 -Xmx1G -最大堆内存;默认大小
    – 默认初始值,物理内存的 64/1 、最大 1/4;按内存8G来算,就是128M ~ 2G
  • 内存回收: 新生代、老年代 - 1:2 ; 新生代:eden 、s1 、s2 - 8:1:1
  • 回收算法:
    – 新生代,复制算法: 将可用的新生代内存分成2个区域 s1 、s2 每次只使用其中一个区域,对其中一个区域进行回收时,将此区域存活的对象复制到另一个区域,把当前区域清空. eden + s1 - > s2 , eden + s2 ->1
    – 老年代,标记压缩:从根节点标记所有被引用的对象,将所有存活对象压缩移动到内存一端,清除剩下垃圾对象清除。

1
2

堆内存溢出( java.lang.OutOfMemoryError )

List 循环添加,一般可能出现在: 大批量添加数据

  • 所有一般,分批量添加List导入数据库
		List<User> list=new ArrayList<>();
        while(true){
            list.add(new User().setName("小树"));
        }

大文件的 IO 处理时:

//size 声明 多 ~~大 ,比如: new byte[in.available()];
byte[] buffer = new byte[size] //1024 * 1024 * 5

异常处理:针对这类型的异常捕捉,用 Throwable e ,然后做出相应处理

		try {
			//TODO
        } catch (Throwable e) {
            e.printStackTrace();
        }

java 虚拟机栈:

  • 线程私有区域,生命周期和线程相同;每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)
  • 存储:栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址
  • 流程:每一个方法被调用直至执行完毕的过程,就对应这一个栈帧在虚拟机栈中从入栈出栈的过程
    –截图取至:地址
  • 参数:-Xss<size>
    1

栈溢出 (java.lang.StackOverflowError):

  • 递归调用方法, 方法调用层次太深,内存不够新建栈帧
private static int count;

    public static void count(){
        try {
            count++;
            count();
        } catch (Throwable e) {
            System.out.println("最大深度:"+count);
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        count();
    }

方法区:java类信息、运行时常量池、静态变量

  • JDK1.7及以前,称为永久代Perm,在堆中。 存储:java类信息、运行时常量池、静态变量、即时编译器编译后的代码等数据
  • JDK 1.8,称为 元空间 MetaSpace,在直接内存中。 存储:类的信息、常量池放到了本地内存中,将常量池和静态变量放到了Java堆里
  • 参数: -XX:MaxMetaspaceSize

程序计数器:保存当前线程锁正在执行的字节码指令的地址;为了线程切换后能恢复到正常执行的位置。

本地方法栈: native方法,JNI,用java 调用 c、c++实现的本地方法库。

### JVM 方法区的概念 JVM 方法区是一个运行数据区域,用于存储已被虚拟机加载的类型信息(类、接口、枚举、注解等)、常量池、静态变量以及即编译器编译后的代码缓存等内容[^1]。方法区属于堆的一部分,在某些 JDK 版本中也被称为永久代(Permanent Generation, PermGen)。然而,自 Java 8 起,永久代被元空间(Metaspace)取代。 ### 工作机制 #### 类型信息管理 当 JVM 加载一个类,该类的结构化信息会被解析并存储到方法区中。这些信息包括字段定义、方法字节码、构造函数以及其他必要的元数据。通过这种方式,方法区成为支持动态链接和反射操作的核心组件之一。 #### 静态变量与常量池 除了类型的元数据外,方法区还负责维护所有的 `static` 变量及其初始值。此外,字符串常量池和其他运行常量也被放置在此区域内。 #### 编译优化 为了提高性能,经过 JIT (Just-In-Time) 编译器处理过的热点代码片段同样可以驻留在方法区内作为本地机器指令形式存在,从而减少重复解释执行带来的开销。 ### 常见问题及解决方案 1. **OutOfMemoryError** 如果应用程序频繁创建新类或者使用大量第三方库,则可能导致方法区耗尽可用内存资源进而抛出 OOM 错误。“java.lang.OutOfMemoryError: Metaspace” 是典型的例子。针对这种情况可通过调整 `-XX:MaxMetaspaceSize` 参数来增加允许的最大容量;对于旧版 JDK 使用的是 `-XX:PermSize` 和 `-XX:MaxPermSize` 来控制 Permanent Generation 大小[^3]。 2. **GC Overhead Limit Exceeded** 当 GC 尝试回收足够的空间失败次数过多触发此异常。这通常表明程序设计存在问题比如循环依赖导致无法释放无用对象等问题。此应该分析 heap dump 文件找出具体原因所在,并考虑重新规划应用架构或调优相关参数如增大年轻代比例(-Xmn),启用 G1 收集算法 (-XX:+UseG1GC)[^4]。 ```bash # 设置最大元空间大小为512M java -XX:MaxMetaspaceSize=512m MyApplication ``` ### 总结 综上所述,理解 JVM 方法区的工作原理有助于开发者更好地诊断复杂环境下的内存泄漏现象,并采取适当措施加以缓解。合理配置 JVM 启动选项能够显著改善系统的稳定性和响应速度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值