vx公众号:
码工是小希
关注选择“星标”
,重磅干货每日 送达!
【如果你觉得文章对你有帮助,欢迎关注,点赞,留言哦
】
礼多人不怪嘛,试着回复下:【路线】,【秘籍】,【实战】,【简历】;
有礼送给你哦🎁 个人主页:是我本人,不来看一下?
好文重点推荐:
❤️故事篇-深入浅出带你搞懂路由协议[ 建议收藏慢慢看 ]
💚“开锁三部曲”——核心基础篇(万字长文安排的明明白白的)
💙《内功修炼系列》之-递归从入门到入土(收藏版)
一 🎂堆的重要概念
堆 针对一个JVM进程来说肯定是唯一的,换句话说就是一个进程只有一个JVM,但我们知道进程包含多个线程,他们是共享同一堆空间的,所以结论是多个线程可以共用同一个JVM的~。
一个JVM实例只对应其一个堆内存,堆也是Java内存管理的核心区域,如上图。
Java堆区在JVM启动的时候即被创建,其空间大小
也就确定了。是JVM管理的最大一块内存空间了。
- 堆内存的大小是可以调节的。
《Java虚拟机规范》规定,堆可以处于物理上不连续的内存空间上,但在逻辑
(地址)上它应该被视为连续的。
所有的线程共享Java堆,在这里还可以划分线程私有的缓冲区(Thread Local Allocation Buffer,TLAB)。
-Xms10m:最小堆内存
-Xmx10m:最大堆内存
下图就是使用:Java VisualVM查看堆空间的内容,通过 jdk bin提供的插件
《Java虚拟机规范》中对Java堆的描述是:
所有的对象实例以及数组都应该在
运行时分配
在堆上。(The heap is the run-time data area from which memory for all class instances and arrays is allocated);
我要说的是:“几乎”
所有的对象实例都在堆上分配内存。我们从实际使用角度看的。
因为还有一些对象是在栈上分配的
其中有部分数组和对象可能永远不会存储在栈上,因为栈帧中保存有引用,这个引用指向对象或者数组在堆中的位置。
在方法结束后,堆中的对象不会马上被移除,仅仅在垃圾收集的时候才会被移除。
也就是触发了GC的时候,才会进行回收
如果堆中对象马上被回收,那么用户线程就会受到影响,因为有stop the word堆,是GC(Garbage Collection,垃圾收集器)执行垃圾回收的重点区域
。
二 📅堆内存细分
Java 7及之前堆内存逻辑上分为三部分:新生区+养老区+永久区
- 新生区 Young/New 又被划分为Eden区和Survivor区
- 养老区 Old/Tenure
- 永久区 Perm
Java 8及之后堆内存逻辑上分为三部分:新生区+养老区+元空间
约定:新生区 -> 新生代 -> 年轻代 、 养老区 -> 老年区 -> 老年代、 永久区 -> 永久代; 变化如下图:
堆空间内部结构,JDK1.8之前从永久代
替换成 元空间
设置堆内存大小与OOM
我们从上面已经知道了,Java堆区是用于存放Java对象实例,那么堆的大小也就是在JVM启动时就已经设定好了,大家可以通过选项"-Xmx"和"-Xms"来进行设置。
- “-Xms"用于表示堆区的起始内存,等价于-xx:InitialHeapSize
- “-Xmx"则用于表示堆区的最大内存,等价于-XX:MaxHeapSize
一旦堆区中的内存大小超过“-xmx"所指定的最大内存时,将会抛出outofMemoryError异常。
通常会将-Xms和-Xmx两个参数配置相同的值,其目的是为了能够在ava垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小
,从而提高性能。
默认情况下
-
初始内存大小:物理电脑内存大小/64
-
最大内存大小:物理电脑内存大小/4
/**
-Xms 用来设置堆空间(年轻代+老年代)的初始内存大小
-X:是jvm运行参数
ms:memory start
-Xmx:用来设置堆空间(年轻代+老年代)的最大内存大小
@create: 2020-07-06-20:44
*/
public class HeapSpaceInitial {
public static void main(String[] args) {
// 返回Java虚拟机中的堆内存总量
long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
// 返回Java虚拟机试图使用的最大堆内存
long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;
System.out.println("-Xms:" + initialMemory + “M”);
System.out.println("-Xmx:" + maxMemory + “M”);
}
}
输出结果:
-Xms:245M
-Xmx:3614M
如何查看堆内存的内存分配情况,命令如下,并配图:
jps -> staat -gc 进程id
-XX:+PrintGCDetails
OutOfMemory举例
我们简单的写一个OOM例子
/**
* OOM测试
*/
public class OOMTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
while(true) {
list.add(999999999);
}
}
}
然后设置启动参数
-Xms10m -Xmx:10m
运行后,就出现OOM了,那么我们可以通过 VisualVM这个工具查看具体是什么参数造成的OOM
年轻代与老年代
存储在JVM中的Java对象可以被划分为两类:
-
一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速,生命周期短的,及时回收即可
-
另外一类对象的生命周期却非常长,在某些极端的情况下还能够与JVM的生命周期保持一致
Java堆区进一步细分的话,可以划分为年轻代(YoungGen)和老年代(oldGen)
其中年轻代又可以划分为Eden空间
、Survivor0空间
和Survivor1空间
(有时也叫做from区、to区)
下面这参数 开发中一般不会调:
-
Eden:From:to -> 8:1:1
-
新生代:老年代 - > 1 : 2
配置新生代与老年代在堆结构的占比。 -
默认-XX:NewRatio=2,表示新生代占1,老年代占2,新生代占整个堆的