JVM学习--(一)Java内存区域与内存溢出异常

Java内存区域与内存溢出异常

一.Java技术体系

JDK:Java程序设计语言、JVM、Java类库三部分所构成 支持Java程序开发的最小的环境
JRE:JVM以及Java类库API的Java SE API子集两部分所构成 支持程序运行的标准环境

二.JVM内存区域分布

Java虚拟机运行时的数据区

1.程序计数器

程序计数器本质上是标记下一条需要执行的字节码指令。特点:
1.线程私有,每个线程都有自己的程序计数器,因此线程安全。
2.如果执行Java方法,记录的是字节码指针的地址;如果执行的是Native方法,计数器值应该为Undefined(本地方法是由C++或者C实现的)。
3.不会发生内存溢出。

2.Java虚拟机栈

虚拟机栈是Java方法执行的线程内存模型。
特点:
1.线程私有,寿命与线程相同。
2.每个方法被执行都有一个栈帧,里面有局部变量表、操作数栈、动态连接、方法出口等。
3.局部变量表存放了各种基本数据类型,空间上以局部变量槽(Slot)表示。
4.栈深度超过允许深度,报StackOverflowError错误;如果是动态扩展,超过内存时报OutofMemoryError。

3.本地方法栈

与虚拟机栈类似,但其中存放的是Native方法。在hotspot中本地方法栈与虚拟机栈是同一个。

4.Java堆

存放对象实例处。
特点:
1.是由垃圾管理器所管理的区域,因此也被称作GC堆。
2.通过将JAVA堆根据线程划分出不同的内存区域,即为TLAB,可以提高分配时的效率。
3.Java堆中的内存可以是连续的,也可以不是连续的。

5.方法区

存储的是被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存。
特点:
1.1.8前存在“永久代”,及Java堆中的一块区域,1.8后将方法区放在元空间,及本地内存中的一片区域(原则上不会发生溢出)。
2.对于方法区的约束比较宽松,可以不进行垃圾回收,但是会有Bug因为未完全垃圾回收产生。

运行时常量池

形式上像键值对,是方法区的一部分,主要存放着编译期生成的各种字面量和符号的引用。
特点:
1.保存的不只是描述的符号引用,还有些符号引用翻译出来的直接引用。
2.具有动态性。即不一定要编译器产生,运行中也可以放入,例如String的intern()方法。

6.直接内存

不是虚拟机数据区的一部分,主要作用是加快JAVA堆和Native堆之间复制数据的速度。

三.HotSpot虚拟机内部对象创造过程

1.当虚拟机遇到new等指令时,先会检查符号指向的类是否被加载、解析和初始化过,若没有,则先初始化。

2.虚拟机为新对象在JAVA堆中分配内存。

1.内存大小的确定以及分配。堆中的主要可以分成三部分:1️⃣对象头。第一类是用于储存对象自身运行时的数据,包括哈希码、GC分代年龄、锁状态等,被称为“Mark Word”,是一个有着动态定义的数据结构。第二类是类型指针。用来指向方法区中的类,但并不是所有对象都需要类型指针。需要注意的是如果是数组,则对象头中必须记录数组长度。2️⃣实例数据。真正储存的信息,所定义的各种类型的字段内容。3️⃣对齐填充。没有含义,任何对象都需要是8的整数倍,需要补齐。
大小基本上是在类结构确定下来的时候就已知了。

2.在Java堆中划分的方法。1️⃣对于连续的内存空间,用“指针碰撞”。2️⃣对于不连续的用“空闲列表”。
保证线程安全的方法:1️⃣进行同步处理,例如CAS。2️⃣采用TLAB。

3.初始化对象

调用CLASS文件中的()方法。

4.对象的访问定位

Java程序会通过栈上的reference来访问。1️⃣将JAVA堆中划分出一块特别的区域当做句柄池。好处是容易改变指针的指向,坏处是时间空间开销变大。2️⃣直接通过对象指针进行访问。

四.实战

1.Java堆溢出

通过-Xms来设置堆的最小值,通过-Xmx来设置堆的最大值
若报Java heap space,主要有两个原因:1️⃣内存泄露,泄露对象与QC Roots或者其他的引用路径相连,导致垃圾回收器无法回收。2️⃣内存不够,适当增大Xmx或者寻找程序中是否有不合理的对象。

2.虚拟机栈和本地方法栈溢出

-Xoss来设置本地方法栈的大小(但是没有任何用)。-Xss来设置栈容量。
若栈允许动态扩展,那么只有当栈容量无法申请到足够的内存时,会抛出OutOfMemoryError。
若栈不允许动态扩展,当栈容量超过最大深度时,会抛出StackOverflowError。
无论是虚拟机栈容量太小或者是栈帧太大,都会抛出错误。
每个栈内存越大,就意味着可以建立的线程越少。

3.方法区和运行时常量池溢出

1.8前可以通过-XX:MaxPermSize来限制永久代的大小来间接限制常量池容量。1.8后方法区在元空间,常量池在Java堆中。
1.6中intern()方法会把首次遇到的字符串实例复制到永久代的字符串常量池中储存,返回的是永久代中的引用的拷贝。但1.6后返回的直接是永久代的引用。
-XX:MaxMetaspaceSize 可以设置元空间大小 -XX:MetaspaceSize 指定元空间的初始空间大小,达到该值会触发垃圾回收 -XX:MinMetaspaceFreeRatio 垃圾收集后控制最小的元空间剩余容量的百分比。

4.直接内存溢出

-XX:MaxDirectMemorySize 可以来设置直接内存的大小。默认与-Xmx大小一致。
一个溢出的明显特征是发现内存溢出之后产生的Dump文件很小,而程序中又间接或者直接使用了DirectMemory。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值