深入理解 JVM 和 GC -- 内存调优

运行时数据区

  • 首先弄清楚一点:一个java进程开启一个jvm,进程又可分成多线程运行
  • 进程在 jvm 上运行时,数据保存在运行时数据区,运行时数据区包括堆、方法栈、虚拟机栈、本地方法区和程序计数器,其中堆和方法栈是线程共享的,虚拟机栈、本地方法区和程序计数器是线程私有的
    运行时数据区

堆(线程共享)

  • 存储程序运行时用到的所有对象,而线程虚拟机栈的局部变量表保存的是对象的引用指针;堆空间分为新生代、老年代、元数据(jdk1.8之后,1.7之前称为永久代),元数据实际上不属于堆空间
    JMM
  1. 为什么分代? 对象的生命周期不一样,GC过后有些被回收有些存活下来,存活下来的对象也不必与新来的对象一起GC,需要另外空间来存放存活下来的对象

  2. 为什么Eden:servivor是8:1:1而不是9:1? Minor GC => 新生代 Major GC => 老年代 一次 Major GC 所花时间 > 一次 Minor GC 所花时间 所以我们希望新生代的对象尽量不要被放到老年代,即 GC
    之后应有98%的对象被回收,如果是9:1的话,对象很容易存活到老年代,所以希望对象在新生代进行更多次的GC以达到目标

  3. 对象分配比例配置:-XX:SurvivorRatio=8

新生代

  • 新生代占堆空间的1/3,分为Eden区(8/10)、From区(1/10)、To区(1/10)
  • 新创建的对象会被放入Eden区,当Eden区空间不足时,虚拟机会做一次 Minor GC,大部分对象会被垃圾回收器回收,剩下的存活的对象放入 From 区,这是复制回收算法
  • 当 From区满时,进行 Minor GC 会对 From 区也做 GC,存活的会进入 To 区,此时 From 区与 To 区角色互换
  • 当 Eden 区再次满的时候, Minor GC 会把存活下来的对象放入 To 区(因为From 区与 To 区已经角色互换)

老年代

  • 当 Minor GC之后 新生代满或放不下新对象时,会触发担保机制,把存活的对象放入老年区
  • 当整个堆空间放满之后,会进行 Full GC,Full GC = Minor GC + Major GC,System.gc() 引起的是 Full GC

元空间

  • JDK1.7之前: 永久代
  • JDK1.8之后: 元空间(直接内存),这样设计是为了解决永久代可能溢出的问题,可以动态扩容,属于堆外内存,可能会挤压堆内内存,所以根据需求定义其大小

堆空间大小的设定

通过GC日志获取多次 full GC 存活下来的活跃数据的平均值
总堆大小 = 活跃数据 * 4
新生代 = 活跃数据 * 1.5
老年代 = 总堆大小 - 新生代
(虚拟机有默认值和自定调整)

内存规整

当多线程进行对Eden区存放堆数据的时候会出现线程安全问题(指针碰撞)
指针碰撞
栈上分配:线程在Eden区有自己的Buffere(可调整),可避免过多的锁,当Buffere不足时会清除到堆内存

堆的调整

-Xms …m

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值