网站首页 > java教程 正文
作者 | 井方哥
地址 | https://zhuanlan.zhihu.com/p/30388521
声明 | 本文是 井方哥 原创,已获授权发布,未经原作者允许请勿转载
概述
我们在进行 Java 开发的时候,很少关心 Java 的内存分配等等,因为这些活都让 JVM 给我们做了。不仅自动给我们分配内存,还有自动的回收无需再占用的内存空间,以腾出内存供其他人使用。但是我们经常面临的一个问题就是内存泄漏,JVM无法完成回收工作,导致内存占用暴涨,最后可能让程序奔溃。本章主要了解下运行时数据区域分布情况以及溢出异常。
说明: 本系列多处摘抄《深入理解Java虚拟机》中内容,主要精简了本书的要点,并叙述自己对本书的理解。本人才疏学浅,文章中有不对的地方,还望批评指教。
运行时数据区域
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 之间来回复制数据
对象
1、New对象过程
new 指令发出
检查 new 的参数是否在常量池中存在这个 Class 的符号引用
检查对应的 Class 是否已经初始化
若没有则先执行初始化过程
分配内存,检查堆是否规整(垃圾收集器是否带有压缩整理功能决定)
规整:指针碰撞方式分配内存
不规整:空闲列表方式分配内存
内存空间初始化为零值(不包括对象头)
对对象进行重要的配置
执行 < init > 方法
2、对象的内存布局
对象头(Mark Word)
自身运行时数据
GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID
类型指针:确定对象是哪个Class的实例
实例数据
存储有效信息,定义的各种字段
相同宽度的字段总是被分配到一起
对齐填充
不一定存在
实例数据没有对齐,需要填充
3、对象的访问定位
句柄(reference):
堆中划分句柄池
句柄地址
到对象实例数据的地址
到对象类型的地址
优势:稳定,对象移动时,(如GC时会移动),这个时候只改变指针地址。句柄信息不变,相对稳定;
指针:
直接存储了上述的对象地址
优势:速度快
OOM
堆溢出:举例一直 new 新的实例对象
栈溢出:举例无限循环调用执行某个方法
方法区和运行时常量池溢出:
String.intern():如果常量池已存在,则返回 String 对象,如果不存在,则先添加到常量池,再返回 String 对象。
动态定义大量的 Class,需要注意内存的回收情况。
小结
程序运行时,需要不停的将数据在内存中分配、计算等。JVM 将不同类型的数据放在不同的位置,这样分工才能够让程序有序的跑起来。我们所定义的方法,以及 new 的对象实例都分别存在方法区和堆中,这两个区域是属于内存共享的地方,也就是说任何线程取的都是一样的。但是,因为有线程的存在。所以,我们需要给线程必要的私有空间。故,在程序运行的时候,我们通过栈来保存该线程自由的局部变量、引用等,通过程序计数器保存了各个线程的执行位置。这样,在线程切换的时候,才能找到自己的上一次执行位置,继续完成未完成的工作。如果,程序执行过程中没有足够的空间分配,就报对应的 OOM 异常。
猜你喜欢
- 2024-11-02 Java堆内存又溢出了!看大师如何防范
- 2024-11-02 并发编程中常见的内存溢出的三种情况
- 2024-11-02 jmeter内存溢出解决方法(jmeter怎么清理缓存)
- 2024-11-02 JAVA服务实例内存高问题排查及解决!牛掰
- 2024-11-02 面试官:你知道JVM内存溢出问题的定位方法吗?
- 2024-11-02 一次大量出现Full GC、内存泄漏问题及内存溢出错误排查和分析
- 2024-11-02 如何识别Java中的内存泄漏(如何识别java中的内存泄漏情况)
- 2024-11-02 java 面试专题一:(Java基础)第三篇常见内存溢出异常问题
- 2024-11-02 JAVA 8 内存溢出总结(java内存溢出会导致应用停机吗)
- 2024-11-02 内存溢出排查思路 内存溢出排查通用思路大概就是这三步了#编程
你 发表评论:
欢迎- 最近发表
-
- Java常量定义防暴指南:从"杀马特"到"高富帅"的华丽转身
- Java接口设计原则与实践:优雅编程的艺术
- java 包管理、访问修饰符、static/final关键字
- Java工程师的代码规范与最佳实践:优雅代码的艺术
- 编写一个java程序(编写一个Java程序计算并输出1到n的阶乘)
- Mycat的搭建以及配置与启动(mycat部署)
- Weblogic 安装 -“不是有效的 JDK Java 主目录”解决办法
- SpringBoot打包部署解析:jar包的生成和结构
- 《Servlet》第05节:创建第一个Servlet程序(HelloSevlet)
- 你认为最简单的单例模式,东西还挺多
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)