JVM总结

一、Java内存模型

线程私有:

虚拟机栈

  • 局部变量表
  • 操作数栈
    • 主要保存计算过程的中间结果,同时作为计算过程中的变量临时的存储空间
  • 动态连接
  • 方法返回地址

本地方法区
程序计数器

线程共有:

  • 方法区:Java方法区和堆一样,方法区是一块所有线程共享的内存区域,他保存系统的类信息。比如类的字段、方法、常量池等。方法区的大小决定系统可以保存多少个类。如果系统定义太多的类,导致方法区溢出。虚拟机同样会抛出内存溢出的错误。
    • 有限分配Eden区–>空间分配担保机制
    • 大对象直接进入老年代–>避免在Eden区和Survivor区之间产生大量的内存复制
    • 对象晋升
      • 年龄阀值 --> -XX:MaxTenuringThreshold, 默认15
      • 动态年龄判定 --> 然而VM并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代;如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代, 而无须等到晋升年龄。

对象生死判定:可达性分析算法

gcroot:

  • 方法区: 类静态属性引用的对象。
  • 方法区: 常量引用的对象。
  • 虚拟机栈(本地变量表)中引用的对象。
  • 本地方法栈JNI(Native方法)中引用的对象。

垃圾回算法3种:

  • 1、标记复制算法
  • 2、标记清除算法
  • 3、标记整理算法

垃圾收集器:

  • 1、serial 串行
  • 2、paranew 并行
  • 3、cms 并法
  • 4、G1
  • 5、ZGC

Serial收集器—Serial Old收集器:简单高效一个线程的收集器,在进行垃圾收集的时候,必须暂停其他所有的线程直到他结
束收集为止。

  • 设置参数:-XX:+UseSerialGC 开启后会使用Serial(Young区用)+ Serial Old(Old区用)的收集器组合,表示:新生代、老年代都会使用串行垃圾收集器,新生代使用复制算法

ParNew收集器:使用多线程进行垃圾回收,在垃圾收集时,会stw暂停其他所有的工作线程ParNew收集器其实就是Serial收集器新生代的并行多线程版本,最常见的应用场景时老年代配合CMS工作

  • 设置参数:-XX:+UserParNewGC 启用ParNew收集器,只影响新生代的收集,不影响老年代开启后ParNew(Young区)+Serial Old收集器组合,新生代使用复制算法,老年代使用标记-整理算法,备注:-XX:ParallelGCThreads限制线程数量,默认开启和CPU数目相同的线程数

Parallel Scavenge收集器:jdk1.8默认垃圾回收器,Parallel Scavenge收集器类似ParNew也是一个新生代垃圾回收器,使用复制算法,也是一个并行的多线程的垃圾回收器,俗称吞吐量优先的垃圾收集器,一句话,穿行收集器在新生代和老年代的并行化。

  • 最关注的是可控的吞吐量:用户代码运行的时间/(用户代码运行的时间+垃圾收集的时间),高吞吐量意味着高效利用cpu的时间,他多用户后台运算而不需要太多的交互任务。
  • 自适应调节策略:这也是ParallelScavenge收集器与ParNew收集器的一个重要区别(自适应调节策略,虚拟机会根据当前运行情况收集性能控制信息,动态调整这些参数以提供最合适的停顿时间(-XX:MaxGCPauseMillis)或最大吞吐量)
  • 设置参数:-XX:+UseParallelGC或-XX:+UseParllelOldGC(相互激活),开启该参数后,新生代使用复制算法老年代使用标记-整理算法

CMS收集器:

  • 优点,并发标记清楚,并发收集地停顿,并发指的是与用户线程一起执行
  • 缺点,CMS在收集与应用线程会同事增加对对内存的占用,也就是CMS必须在老年代用尽之前完成垃圾回收,否则CMS回收失败时会出发担保机制,串行老年代收集器将会以STW的方式进行一次GC,从而造成较大的停顿时间
  • 标记-清除:产生大量空间碎片,-XX:CMSFullGCsBeForeCompaction来设置多少次full gc之后进行一次整理;
  • 参数设置:-XX:+UseConcMarkSweepGC 开启该参数后,会自动将XX:+UserParNewGC打开开启参数后:ParNew(Young区用)+CMS(Old区用)+Serial Old的收集器组合(CMS出错后的备用收集器)
    • 初始标记—> STW. 只是标记一下GC Roots能直接关联的对象,速度很快,仍然需要暂停所有的工作线程
    • 并发标记—> 和用户线程一起工作,不需要暂停工作线程,主要标记过程,标记全部对象
    • 重新标记—> STW. 对之前标记的对象进行二次确认
    • 并发清除—> 和并发标记这两个阶段是最耗时的

特点:CMS收集器是一种以获取最短回收停顿时间为目标的收集器,这类应用尤其重视服务器的响应速度,希望停顿时间最最短,CMS非常适合对内存大,CPU核数多的服务器端应用,也是G1出现之前大型应用首选的垃圾回收器
缺点:无法处理浮动垃圾,标记-清除会产生大量空间碎片,当然可以设置多少次full gc之后进行一次整理,=0的情况下,就变成了标记-整理

G1收集器:

  • G1将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念, 但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合.
    • E:Eden区
    • S:Survivor区
    • O:Old区
    • H:超大对象区,如果一个对象占用的空间超过了分区容量的50%以上,G1收集器就认为这是一个巨型对象,这些巨型对象默认会直接分配到老年代,但是如果他是一个短期存在的巨型对象,就会对垃圾收集器产生负面影响,为了解决这个问题,G1划分了多了个H区,专门用来存储巨型对象如果一个H区存不下,会寻找多个连续的H区来存储,有时候没有连续的空间,就进行full GC
      • 并发标记阶段:在与应用程序并发执行的过程中会计算活跃度信息.这些活跃度信息标识出那些regions最适合在STW期间回收不像CMS有清理阶段.
      • 再次标记阶段:使用Snapshot-at-the-Beginning(SATB)算法比CMS快得多.空region直接被回收.
      • 拷贝/清理阶段:年轻代与老年代同时回收,老年代内存回收会基于他的活跃度信息.

G1收集器相对于CMS收集器的优点

  • G1在压缩空间方面有优势
  • G1通过将内存空间分成区域(Region)的方式避免内存碎片问题,Eden, Survivor, Old区不再固定、在内存使用效率上来说更灵活
  • G1可以通过设置预期停顿时间(Pause Time)来控制垃圾收集时间避免应用雪崩现象
  • G1在回收内存后会马上同时做合并空闲内存的工作、而CMS默认是在STW(stop the world)的时候做
  • G1会在Young GC中使用、而CMS只能在O区使用

JVM命令:

  • javap:查看经javac之后产生的JVM字节码代码
  • jps:打印Hotspot VM进程 VMID、JVM参数、main()函数参数、主类名/Jar路径
  • jstat:查看Hotspot VM 运行时信息 类加载、内存、GC[可分代查看]、JIT编译
  • jinfo:查看和修改虚拟机各项配置
  • jmap:heapdump: 生成VM堆转储快照、查询finalize执行队列、Java堆和永久代详细信息
  • jstack:查看VM当前时刻的线程快照: 当前VM内每一条线程正在执行的方法堆栈集合

jconsole 可以查看内存、线程、类、CPU信息, 以及对JMX MBean进行管理
jvisualvm 可以监控内存泄露、跟踪垃圾回收、执行时内存分析、CPU分析、线程分析

JVM error:

  • java.lang.StackOverflowError:递归调用,容易导致栈溢出
  • java.lang.OutOfMemoryError:Java heap space:堆空间不足导致Java heap spacke
  • java.lang.OutOfMemoryError:Direct buffer memory:nio磁盘不足导致direct buffer memory
  • java.lang.OutOfMemoryError:GC overhead limit exceeded:频繁垃圾回收,却没有释放空间,导致Gc overhead limit execpte
  • java.lang.OutOfMemoryError:unable to create new native thread高并发unable create new native thread
  • java.lang.OutOfMemoryError:Metaspace:元空间大小,只受本地内存限制
  • java.lang.OutOfMemoryError: PermGen space:类或者方法不能被加载到老年代。它可能出现在一个程序加载很多类的时候,比如引用了很多第三方的库
  • java.lang.OutOfMemoryError: Requested array size exceeds VM limit:创建的数组大于堆内存的空间
  • java.lang.OutOfMemoryError: request bytes for . Out of swap space:同样是本地方法内存分配失败,只不过是JNI或者本地方法或者Java虚拟机发现;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值