JVM内存管理

分代策略

Java Heap java虚拟机中管理内存的最大一块 堆是被所有线程共享的内存区域。在虚拟机启动的时候创建。
所有的对象实例以及数组都要在堆上分配。
栈帧只能存储指向堆中的对象和数组的引用。 对象只能由垃圾回收器移除。

Java堆是收集器管理的主要区域, GC堆 采用的分代收集的算法。

  • 新生代
    经常被分为 Eden空间和Survivor空间
  • 老年代
  • 永久代
    1.8 的jdk之后,就不存在永久代了。 被metaspace替代

在细致的去分

  • Eden空间
  • FromSurvivor空间
  • ToSurvivor 空间

进一步划分的目的是为了更好的回收内存或者更快的分配内存。

在这里插入图片描述

1.8 的jdk之后,就不存在永久代了。 被metaspace替代了, 采用永久代的方式来实现方法区,永久代 用于放常量 类的信息 静态变量等数据 这些数据和垃圾回收的关系不是很大,
和垃圾回收关系比较大的两个区域分别是新生代和老年代。

在这里插入图片描述

新生代,老年代,永久代

新生代

  • 新生成的对象优先的放在新生代中,存活率很低
  • 新生代中的常规应用进行一次垃圾回收, 可以回收到70%-95% 效率很高

HotSpot 将新生代划分为三个区

  • 一块比较大的 Eden 伊甸
    和两块较小的 Survivor 幸存者
    默认比例为:8:1:1
    为什么用这个比例 原因是因为hotspot 采用的复制算法来回收新生代。
    大的对象是不会进入新生代的 而是直接进入老年代

  • 当Eden区没有足够的空间进行分配,虚拟机将发起一次MinorGC

  • GC开始的时候 对象会存在Eden区 。 这个时候FormSurvivor区和ToSurvivor 是空的,
    作为保留区域。

  • GC进行时 Eden区的所有存活的对象都会被复制到ToSurvivor 区中 而在FormSurvivor区,扔存活的对象,根据他们的年龄值决定去向。年龄值达到年龄的阀值(15 新生代中的对象每熬过一次GC 年龄就+1)的对象会被移动到老年代中,没有达到阀值的对象会被复制的ToSurvivor区,接着FormSurvivor和ToSurvivor 两者交换角色。都要保证ToSurvivor 区在一轮GC后是空的 GC时 当ToSurvivor没有足够的空间存放上一次新生代收集下来的存活对象,需要依赖老年代进行分配担保,将这些对象放入到老年代。

老年代

  • 在新生代经历了多次GC(具体要看虚拟机的配置的情况)后仍然存活了下来的对象就会进入老年代,对象的生命周期长,存活率高,在老年代进行GC的频率比较低,而且回收速度也比较慢。

永久代 PermanentGeneration — metaspace

  • 类的信息 常量 静态变量 JIT即时编译器编译后的代码等数据。这个区域是不进行垃圾回收的。

方法区

方法区的演进过程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.8jdk 版本之后永久代被移出 由metaspace来替代,元空间是没有上限

方法区结构

在这里插入图片描述

  • 方法区 Methodarea 与java中堆一样的,方法区中的数据可以被每个线程共享,用于存储已经被加载的类的信息 ,常量 静态变量 ,以及JIT即时编译器的代码数据 等等。JVM虚拟机的规范中,把方法区描述为堆的一个逻辑部分,也有一个名字来对应 Non-heap 是为了与java中的堆进行分区。

  • 很多人把方法区称为永久代, 本质上其实是不一样,按照HotSpot设计团队选择把GC分代收集扩展至方法区,永久代知识方法区的实现而已 目前永久代被废除 叫metaspace

  • Java的虚拟机 对方法区的限制是比较宽松的,除了和java堆一样不需要连续的内存 也可以选择固定的大小或者可以扩展,还可以选择不实现垃圾回收。垃圾回收在方法区中出现的比较少。

  • 方法区 内存回收目标是对常量池的回收和对类型的卸载。

常量池

  • 1.静态常量池 就是.class文件中的常量池, 包含字符串数字这些字面量,还包含 类的方法的信息, 占用了classfile的大部分空间,这个常量池主要用于放两大类的常量:字面量和符号引用,包含了三种类型的常量 类和接口的全限定名 字段名称和描述符,方法名称和描述符。

  • 2.运行时常量池
    虚拟机会将各个的class文件中常量池载入到运行时常量池中 即编译期间 生成字面量 符号引用,装载class文件

在永久代移出之后,运行时常量池搬到了元空间里, 它是装静态变量 字节码的信息 有它的地方才被称为方法区。

  • 3.字符串常量池 StringTable
    可以把它理解为运行时常量池分出来的一部分内容,加载时 对于class的静态常量池 字符串会被装载到字符串常量池中。

在永久代移出之后,字符串的常量池也不再放在永久代了, 也没有放入到元空间里,而是继续留在了堆空间了

  • 4.整型常量池 Integer 类似StringTable 可以把它理解为运行时常量池分出来的一部分内容 在加载时如果是整型的数据被装到整型的常量池中

在这里插入图片描述

所有的线程共享同一个方法区,因为访问方法区的数据和动态链接的进程必须是线程安全的,
方法区存储了每个类的信息,可以通过this.getClass().getClassLoader()进行获取

直接内存

在这里插入图片描述

  • 直接内存并不属于JVM的内存结构,它是物理机的内存,但是JVM虚拟机可以调用该部分内存。
    在这里插入图片描述

NIO(New input/output)是JDK1.4中新加入的类,引入了一种基于通道(channel)和缓冲区(buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过堆上的DirectByteBuffer对象对这块内存进行引用和操作。

  • 直接内存 DirectMemory 并不是虚拟机运行时数据区的一部分,也不是规范中定义的内存区域,但它也会OOM。
  • JDK1.4 加入了NIO new Input/Output 引入了一种基于通道 Channel 与缓冲区buffer的IO的方式。可以使用native函数库直接分配堆外的内存,然后通过Java的对象DirectByBuffer对象作为这块内存的引用而进行操作。在某些场景中是可以提高性能的,因为它避免了在java堆和native堆中来回的复制数据。
  • 直接内存分配是不受java的堆的大小控制 ,但他还是内存,只要是内存就有限制,一般情况下我们使用-Xmx 等一些参数的收,会忽略直接内存。 从而导致动态扩展的时候内存总和大于物理内存限制,而出现OOM。
  • 直接内存分配内存通过java中的unsafe对象分配一块直接内存,直接内存大小在分配时指定。直接内存由于不受JVM的管理,所以直接内存的释放,必须主动调用unsafe对象进行释放,才能将直接内存释放。

JVM常用调优参数

-Xms:初始堆大小
-Xms:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3表示年轻代和年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如3表示Eden: 3 Survivor:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小

收集器设置

-XX:+UseSerialGC:设置串行收集器
 -XX:+UseParallelGC:设置并行收集器
 -XX:+UseParalledlOldGC:设置并行年老代收集器
 -XX:+UseConcMarkSweepGC:设置并发收集器

垃圾回收统计信息

-XX:+PrintGC 
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename

并行收集器设置


XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数
-XX:MaxGCPauseMillis=n:设置并行收集最大的暂停时间(如果到这个时间了,垃圾回收器依然没有回收完,也会停止回收)
-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为:1/(1+n)
-XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况
-XX:ParallelGCThreads=n:设置并发收集器年轻代手机方式为并行收集时,使用的CPU数。并行收集线程数

来自

堆大小设置

java-Xmx3550m -Xms3550m-Xmn2g -Xss128k
-Xmx3550m:设置JVM最大可用内存为3550m
-Xms3550m:设置JVM初始内存为3550m,此值可以设置-Xmx相同,以避免每次垃圾回收完成以后JVM重新分配内存
-Xmn2g:设置年轻代大小为2G。整个堆大小=年轻代大小+年老代大小+持久代大小。持久代一般固定为64M,所以增大年轻代后,将会减少年老代大小,此值对系统性能影响比较大,Sun官方推荐配置为整个堆的3/8
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程栈大小为1M,以前每个线程堆栈大小为256k。根据应用的线程所需要内存大小进行调整。在相同物理内存下,减少这个值能够生成更多的线程。但是操作系统对一个进程内的线程还是有限制的,不能无限生成,经验值在3000-5000左右
java -Xmx3550m-Xms3550m-Xss128k-XX:NewRatio=4-XX:SurvivorRatio=4
-XX:MaxPermSize=16m-XX:MaxTenuringThreshold=0

-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代和年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,提高效率,如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活时间。

吞吐量优先的并行收集器

java -Xmx3550m-Xms3550m-Xss128k-XX:+UseParallelGC
-XX:ParallelGCThreads=20
-XX:+UseParallelGC:选择垃圾收集器为并行收集器。次配置仅对年轻代有效。即上述配置下,年轻代使用并行收集,而年老代仍旧使用串行收集。
-XX:PARALLELgcThreads=20:配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相同。
-XX:+UseParallelOldGC:配置年老代来及收集方式为并行收集,JDK6.0支持对年老代并行收集
-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值
-XX:+UseAdaptiveSizePolicy:设置此选项以后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低响应时间或者收集频率等,此值建议使用并行收集器时,一直打开

响应时间优先的并发收集器

-XX:CMSFullGCsBeforeCompaction=5
-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction:由于并发收集器不对内存空间进行压缩、整理、所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理
-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

来自

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值