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。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能