为了账号安全,请及时绑定邮箱和手机立即绑定

深入理解Java虚拟机02--Java内存区域与内存溢出异常

标签:
Java

一.概述

  我们在进行 Java 开发的时候,很少关心 Java 的内存分配等等,因为这些活都让 JVM 给我们做了。不仅自动给我们分配内存,还有自动的回收无需再占用的内存空间,以腾出内存供其他人使用。但是我们经常面临的一个问题就是内存泄漏,JVM无法完成回收工作,导致内存占用暴涨,最后可能让程序奔溃。本章主要了解下运行时数据区域分布情况以及溢出异常。

二.运行时数据区域

https://img1.sycdn.imooc.com//5b74517400017a0804930380.jpg

1、程序计数器

  • 线程私有

  • 当前线程所执行的字节码的行号指示器

  • Java 多线程是通过再一个内核中轮流执行实现的,计数器就保证了切换线程的时候可以回到原来正确的执行位置

  • 程序计数器必须每个线程单独一个,是线程私有的内存区域

  • 程序计数器是唯一一个 JVM 没有规范 OutOfMemoryError 的区域

2、Java虚拟机栈(java方法)

  • 线程私有

  • Java方法执行的内存模型,即方法执行时会创建一个栈帧,保存了需要的局部变量表、操作数栈、动态链接、方法出口等信息;

  • 线程请求的栈深度>JVM允许的深度时,报StackOverflowError;

  • 大多数的 JVM 可以动态扩展内存,如果无法申请到足够的内存时,报 OutOfMemoryError;

3、本地方法栈(native方法)

  • 同Java虚拟机栈

4、Java堆

  • 线程共享

  • 唯一目的:存放对象实例

  • 分类:新生代、老生代,或者 Eden 空间、From Survior 空间、To Survivor 空间

  • 分类目的:更好的回收和分配内存

  • 没有内存完成实例分配,或者不能再扩展,报OutOfMemoryError 异常

  • 可以自己配置大小(-Xmx和-Xms)

5、方法区

  • 线程共享

  • 目的:存储类信息、常量、静态变量、即时编译器编译后的代码等数据;

  • 该区内存回收目标:主要针对常量池的回收和对类型的卸载;

  • 无法满足内存分配要求时,报 OutOfMemoryError 异常

6、运行时常量池

  • 注意:运行时常量池属于方法区

  • 目的:存储编译期生成的各种字面量和符号引用

  • 特征:并非只有编译期置入 Class 文件中的常量池内容才能进入运行时常量池,在运行期间也可以置入新的常量,比如 String 的intern() 方法;

  • 无法申请足够内存时,报 OutOfMemoryError 异常

 

三.直接内存

  • 非运行时数据区域内存

  • Native 函数分配堆外内存,堆内的 DirectByteBuffer 作为这块内存的引用

  • 性能显著提高,避免了 Java 堆和 native 之间来回复制数据

四.HotSpot虚拟机对象探秘

1、New对象过程

  • new 指令发出

  • 检查 new 的参数是否在常量池中存在这个 Class 的符号引用

  • 检查对应的 Class 是否已经初始化

  1. 若没有则先执行初始化过程

  • 分配内存,检查堆是否规整(垃圾收集器是否带有压缩整理功能决定)

  1. 规整:指针碰撞方式分配内存

  2. 不规整:空闲列表方式分配内存

  • 内存空间初始化为零值(不包括对象头)

  • 对对象进行重要的配置

  • 执行 < init > 方法


2、对象的内存布局

对象头(Mark Word)

  • 自身运行时数据

  1. GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID

  2. 类型指针:确定对象是哪个Class的实例

实例数据

  • 存储有效信息,定义的各种字段

  • 相同宽度的字段总是被分配到一起

对齐填充

  • 不一定存在

  • 实例数据没有对齐,需要填充


3、对象的访问定位
句柄(reference):

  • 堆中划分句柄池

  • 句柄地址

  1. 到对象实例数据的地址

  2. 到对象类型的地址

  • 优势:稳定,对象移动时,(如GC时会移动),这个时候只改变指针地址。句柄信息不变,相对稳定;

指针:

  • 直接存储了上述的对象地址

  • 优势:速度快

五.OOM

  • 堆溢出:举例一直 new 新的实例对象

  • 栈溢出:举例无限循环调用执行某个方法

  • 方法区和运行时常量池溢出:

  1. String.intern():如果常量池已存在,则返回 String 对象,如果不存在,则先添加到常量池,再返回 String 对象。

  2. 动态定义大量的 Class,需要注意内存的回收情况。

原文出处:https://www.cnblogs.com/ganchuanpu/p/9428688.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消