jvm内存模型与GC的认识

一、JVM内存模块

1.方法区(线程共享)
  方法区存储的是:常量池、静态变量(static)以及方法信息(修饰符、方法名、返回值、参数等)、类信息(类变量)等

2. 堆(线程共享)
  概念:堆是线程共享的内存区域,它是虚拟机管理内存中最大的一块。
  堆储存的是:实例对象。比如 A a1 = new A();a1就是实例对象。A a2;a2就是类对象。
  GC主要在新生区(伊甸园区)、老年区
  新生区(伊甸园区(对象都是在这个区new出来的)、幸存区to、幸存区from:幸存区位置会互相交换,谁空谁是to)
  老年区

3.栈(虚拟机栈-线程隔离)
  概念:又名堆栈,主管程序运行,生命周期和线程同步,线程结束,栈内存就释放了。不存在垃圾回收问题。
  虚拟机栈储存的是:8大基本类型 + 对象引用 + 实例方法

4.栈(本地方法栈-线程隔离)
  本地方法栈储存的是:本地接口库里调用的方法,就是java里面native关键字修饰的方法。
  凡是带native关键字的,说明java的作用范围达不到了,回去调用底层c/c++语言的库,首先会进入本地方法栈,然后到本地方法接口。

5.程序计数器(线程隔离)
  每个线程启动是都会创建一个程序计数器,保存的是正在执行的jvm指令,程序计数器总是指向下一条将被执行指令的地址。生命周期与线程的生命周期保持一致。


二、OutOfMemoryError内存溢出和StackOverFlowError栈溢出及解决方法

说明:不论是内存溢出还是栈溢出,简单来说就是你放的太多了空间不够了,溢出来了,这样就比较好理解了。

(1) OutOfMemoryError内存溢出(OOM)
  原因:
    ①是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于能提供的最大内存。
    ②由于长期保持某些资源的引用,垃圾回收器无法回收它,从而使该资源不能够及时释放,也称为内存泄露。
  解决:
    ①设置JVM的堆参数
    -Xmx:JVM最大内存
    -Xms:启动初始内存
    -Xmn:新生代大小
    -Xss:每个线程虚拟机栈及堆栈的大小 )
    例如:-Xms1024m -Xmx1024m -Xmn512m -Xss5m。
    ②分析内存,看一下那个地方出现了问题(专业工具:Jprofiler,MAT)分析Dump内存文件, 快速定位内存泄漏,
    怎么查找dump文件,直接找到文件的文件夹打开获得大的对象。
    制造堆溢出:一直死循环new对象就ok了。

  –Xms:JVM初始分配的堆内存,默认是物理内存的1/64。
  –Xmx:JVM最大允许分配的堆内存,默认是物理内存的1/4。
  –Xmn:堆内新生代的大小。通过这个值也可以得到老生代的大小:-Xmx减去-Xmn。官方推荐为堆的1/3
  –Xss:规定了每个线程虚拟机栈及堆栈的大小,一般情况下,256k是足够的,此配置将会影响此进程中并发线程数的大小。

(2) StackOverFlowError栈溢出
  原因:
    线程请求分配的栈容量超过Java虚拟机栈允许的最大容量。
  解决:
    ①修改代码
    ②增加线程堆栈大小(-Xss)。

  制造栈溢出:一直死循环调用方法就行。

三、轻GC(Minor GC)和重GC(Full GC)

  Minor GC
    当新对象去伊甸园区(Eden)申请内存失败的时候,就会进行Minor GC; 对伊甸园区(Eden)回收非存活对象,而没有被回收的对象,
    会进入幸存区(Survivor),这种GC只发生在伊甸园区(Eden),不会影响到老年区。
    因为新对象分配内存大部分都在伊甸园区(Eden),所以伊甸园区(Eden)GC比较频繁。
    默认内存大小:Eden:survivor:survivor=8:1:1
    新生代是 GC 收集垃圾的频繁区域。
    当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域所容纳
    ( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),
    则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,
    然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,
    就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代。

  Full GC
    清理整个堆,因为Full GC需要对整个堆进行回收,所以比Minor GC慢,因为我们要尽可能的减少Full GC的次数。
    我们所说的JVM调优,很大一部分就是对Full GC的优化。

  以下情况会造成 Full GC:
    老年区满了:年轻区的对象转入或创建大对象才会满。
    持久区满了(jdk7及之前版本)
    方法区满了(jdk8及之后版本):系统中要加载的类过多。
    System.gc() 被显示调用
    通过Minor GC后进入老年代的平均大小大于老年代的可用内存:第一次Minor GC之后,有2MB的对象转入老年区,
    然后在下一次Minor GC的时候就会判断老年区的空间是否有2MB,如果没有就进行Full GC。

posted @   美宰可#F22  阅读(310)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
点击右上角即可分享
微信分享提示