做淘宝客需要建网站吗,国内优秀个人网站,嵌入式软件工程师培训,直播软件的appJVM 常用参数设置积累
# 堆的初始值#xff0c;默认物理内存的1/64
-Xms:
# 堆的最大值#xff0c;默认物理内存的1/4
-Xmx:
# 年轻代大小「在整个堆内存大小确定的情况下#xff0c;增大年轻代将会减小年老代#xff0c;反之亦然。此值关系到JVM垃圾回收#xff0c;对系…
JVM 常用参数设置积累
# 堆的初始值默认物理内存的1/64
-Xms:
# 堆的最大值默认物理内存的1/4
-Xmx:
# 年轻代大小「在整个堆内存大小确定的情况下增大年轻代将会减小年老代反之亦然。此值关系到JVM垃圾回收对系统性能影响较大官方推荐配置为整个堆大小的3/8」
-Xmn:
# 设置年轻代初始值为 1024 M
-XX:NewSize1024
# 设置年轻代最大值为 1024 M
-XX:MaxNewSize1024m
# 设置线程栈大小设置越小说明一个线程栈里面能分配的栈帧数就越少但对于 JVM 来讲能开启的线程数就越多
-Xss128k
# 方法区大小设置「jdk1.8 之后使用元空间替换了方法区也使用了其他命令」
-XX:MaxPermSize
# 元空间大小设置
-XX:MetaspaceSize
-XX:MaxMetaspaceSize
# 设置大对象的大小如果对象超过设置大小会直接进入老年代不会进入年轻代「只在 Serial 和ParNew两个收集 器下有效」
-XX:PretenureSizeThreshold1000000
# 设定对象晋升到老年代的年龄阈值「设定能经历 10 次拷贝对象则晋升至老年代」
-XX:MaxTenuringThreshold10
# jdk1.8 默认设置了下述参数设置该参数就会在每次 minor gc 之前看看老年代的可用内存大小是否大于之前每一次 minor gc 后进入老年代的对象的平均大小如果小于则那么就会触发一次 Full gc
-XX:-HandlePromotionFailure
JVM 排查问题命令积累
# 查询实例个数和占用空间大小
jmap -histo pid
# 导出堆内存信息
jmap -dump:formatb,filetest.hprof pid
# 查找死锁打印出线程的状态
jstack pid
# 查看当前运行 java 应用的扩展参数
jinfo pid
# 查看内存中各个部分的使用情况「eden、survivor、old」
jstat -gc pid
# 堆内存统计
jstat -gccapacity pid
# 新生代内存统计
jstat -gcnewcapacity pid
# 新生代垃圾回收统计
jstat -gcnew pid
# 老年代内存统计
jstat -gcoldcapacity pid
# 老年代垃圾回收统计
jstat -gcold pid
# 元数据空间统计
//加入Java开发交流君样593142328一起吹水聊天
jstat -gcmetacapacity pid
# 总结垃圾回收统计
jstat -gcutil pid
JVM 的运行模式有三种
解释模式「-Xint」只使用解释器执行一行字节码就编译一次机器码「不会去缓存」 优点启动块缺点整体执行相比编译模式慢 编译模式「-Xcomp」只使用编译器现将所有的字节码文件一次性编译为机器码然后一次性去执行所有的机器码 优点好处是执行快缺点启动比解释模式慢 混合模式依旧采用解释模式执行代码但是对于一些“热点”代码采用编译模式执行JVM 一般采用混合模式执行代码 优点相比解释模式执行会快相比编译模式启动会快 针对混合模式JVM 有对应的技术去实现比如 JIT也就是即时编辑技术。
JVM 内存分配与回收
jvm 内存区域图 堆
堆内内存堆内内存 年轻代 老年代 持久代『jdk1.8 之后没有持久代』
优点缺点
堆外内存把内存对象分配在 Java 虚拟机的堆以外的内存「比如java.nio.DirectByteBuffer」
优点 减少了垃圾回收机制(GC 会暂停其他的工作)加快了复制的速度「堆内在flush到远程时, 会先复制到直接内存非堆内存, 然后再发送而堆外内存本身就是物理机内存几乎省略了该步骤」。 缺点 内存难以控制「使用了堆外内存就间接失去了JVM管理内存的可行性改由自己来管理当发生内存溢出时排查起来非常困难」。 年轻代
伊甸区survivor from 区survivor to 区 811
伊甸区
大部分对象都在这里诞生当Eden区满时, 依然存活的对象将被复制到Survivor区, 当一个Survivor 区满时, 此区的存活对象将被复制到另外一个Survivor区
survivor 区
survivor from 区survivor to区
老年代
方法区/元空间
在 jdk1.8 之后取消了方法区命名为元空间
线程栈
什么场景下对象会进入老年代
即将存储的大对象在eden 区域是发现存储不下「就算 Minor gc后还是存储不下」长期存活下来的对象Minor gc 后存活的对象Survivor区放不下
什么是老年代空间分配担保机制 年轻代每次 minor gc 之前 JVM 都会计算下老年代剩余可用空间如果这个可用空间小于年轻代里现有的所有对象大小之和包括垃圾对象就会看一个 “-XX:-HandlePromotionFailure”jdk1.8 默认就设置了的参数是否设置了如果有这个参数就会看看老年代的可用内存大小是否大于之前每一次 minor gc 后进入老年代的对象的平均大小。 如果上一步结果是小于或者之前说的参数没有设置那么就会触发一次 Full gc对老年代和年轻代一起回收一次垃圾如果回收完还是没有足够空间存放新的对象就会发生 OOM当然如果 minor gc 之后剩余存活的需要挪动到老年代的对象大小还是大于老年代可用空间那么也会触发 full gcfull gc完之后如果还是没用空间放 minor gc 之后的存活对象则也会发生 “OOM”。 触发 full gc 的时机 调用System.gc()老生代内存不足的时候即将要放进老年代的对象过大需要进行老年代回收「和第二种类似」老年代空间分配担保机制中有可能触发「也就是老年代空间分配担保失败」执行 jmap -histo:live 或者 jmap -dump:live 的时候 如何判断对象可以被回收
1. 引用计数法 给对象添加一个引用计数器没增加一个地方引用它计数器就加一减少一个计数器就减一但是解决不了循环引用的问题「会导致内存泄露」主流的虚拟机都没有使用这个。 2. 可达性分析 通过一系列的称为 GC Roots 的对象作为起点从这些节点开始向下搜索找到的对象都标记为非垃圾对象其余未标记的对象都是垃圾对象。 GC Roots 根节点线程栈的本地变量、静态变量、本地方法栈的变量等等。
具体操作从gc root根往下搜索然后三色标记黑灰白刚开始是白色如果搜索到A节点A节点的子节点还没被搜索则A节点是灰色A节点包括子节点全部搜索完毕标记为黑色到最后白色的就回收了
3. 依据引用类型 java的引用类型一般分为四种强引用、软引用、弱引用、虚引用。
强引用 普通的变量引用
public static Person person new Person();
软引用 将对象用 SoftReference 软引用类型的对象包裹正常情况不会被回收但是GC做完后发现释放不出空间存放新的对象则会把这些软引用的对象回收掉。软引用可用来实现内存敏感的高速缓存。
public static SoftReferencePerson person new SoftReferencePerson(new Person());
弱引用 将对象用 WeakReference 软引用类型的对象包裹弱引用跟没引用差不多GC 会直接回收掉很少用。
public static WeakReferencePerson person new WeakReferencePerson(new Person());
虚引用 虚引用也称为幽灵引用或者幻影引用是最弱的一种引用关系几乎不用。 一个对象是否有虚引用的存在完全不会对其生存时间构成影响也无法通过虚引用来获取一个对象的实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。虚引用和弱引用对关联对象的回收都不会产生影响如果只有虚引用活着弱引用关联着对象那么这个对象就会被回收。它们的不同之处在于弱引用的get方法虚引用的get方法始终返回null弱引用可以使用ReferenceQueue虚引用必须配合ReferenceQueue使用。 jdk中直接内存的回收就用到虚引用由于jvm自动内存管理的范围是堆内存而直接内存是在堆内存之外其实是内存映射文件自行去理解虚拟内存空间的相关概念所以直接内存的分配和回收都是有Unsafe类去操作java在申请一块直接内存之后会在堆内存分配一个对象保存这个堆外内存的引用这个对象被垃圾收集器管理一旦这个对象被回收相应的用户线程会收到通知并对直接内存进行清理工作。
4. 通过 finalize() 方法最终判定对象是否存活
即使在可达性分析算法中不可达的对象也并非是“非死不可”的这时候它们暂时处于“缓刑”阶段要真正宣告一个对象死亡至少要经历再次标记过程。标记的前提是对象在进行可达性分析后发现没有与 GC Roots 相连接的引用链。第一次标记并进行一次筛选。 筛选的条件是此对象是否有必要执行 finalize() 方法。当对象没有覆盖 finalize 方法对象将直接被回收。第二次标记 如果这个对象覆盖了 finalize 方法finalize 方法是对象脱逃死亡命运的最后一次机会如果对象要在 finalize() 中成功拯救自己只要重新与引用链上的任何的一个对象建立关联即可譬如把自己赋值给某个类变量或对象的成员变量那在第二次标记时它将移除出“即将回收”的集合。如果对象这时候还没逃脱那基本上它就真的被回收了。
垃圾回收算法 1、标记-清除算法
分为两个阶段即标记和清除首先会标记所有需要被回收的对象在标记完成后统一对已经标记的对象进行回收是最基础的收集算法。
优点
实现简单
缺点
内存碎片化效率不高
使用场景主流虚拟机不使用
2、标记-整理算法「也叫标记-压缩算法」
针对老年代进行回收的一种算法标记的过程和『标记-清除算法』一样只是在清除完成后会将还存活的对象朝着一个方向移动然后固定的清理靠近边界的对象。
优点
解决了碎片化
缺点
效率不高移动了对象地址需要更新对象的引用
使用场景用于老年代垃圾回收
3、复制算法「比标记清理和标记整理快 10 倍以上」
能解决「标记-清理算法」带来碎片化问题复制算法首先将内存分为大小相同的两块每次只使用其中的一块但这一块被使用完后「或者是没法提供所需的连续长度的内存」就会将这一块的内存复制到另一块去然后再一次性将这块的内存空间全部清理掉。
优点
解决了碎片化效率高
缺点
内存使用率不高
使用场景用于年轻代垃圾回收
4、分代回收算法 这种算法不是新鲜的算法而是针对不同的内存分区采用不同的回收算法比如在新生代中每次收集都会有大量对象近 99%死去所以可以选择复制算法只需要付出少量对象的复制成本就可以完成每次垃圾收集。而老年代的对象存活几率是比较高的而且没有额外的空间对它进行分配担保「老年代多是大对象很可能是需要连续内存地址的对象」所以我们必须选择「标记清除算法」或「标记整理算法」进行垃圾收集。 垃圾收集器「回收算法的具体实现」 1、Serial 收集器「-XX:UseSerialGC -XX:UseSerialOldGC」 新生代采用复制算法老年代采用标记-整理算法 Serial串行收集器是最基本、历史最悠久的垃圾收集器了。是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程「也就是应用程序线程」直到它收集结束。 Serial 收集器执行过程 优点 没有多线程交互单线程实现简单相比其他单线程收集器效率最高「当然是比不上多线程收集器」 缺点 STW 时间长用户体验不好 使用场景 一种用途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使用另一种用途是作为 CMS 收集器的后备方案。 2、ParNew 收集器「-XX:UseParNewGC」 新生代采用复制算法老年代采用标记-整理算法 ParNew 收集器其实就是 Serial 收集器的多线程版本除了使用多线程进行垃圾收集外其余行为控制参数、收集算法、回收策略等等和 Serial 收集器完全一样。默认的收集线程数跟 CPU 核数相同当然也可以用参数-XX:ParallelGCThreads指定收集线程数但是一般不推荐修改。 ParNew 收集器执行过程 优点 相比 Serial 效率高 缺点 实现稍复杂 使用场景
3、Parallel 收集器「-XX:UseParallelGC(年轻代) -XX:UseParallelOldGC(老年代)」 新生代采用复制算法老年代采用标记-整理算法 Parallel Scavenge 收集器关注点是吞吐量高效率的利用CPU。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间提高用户体验。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。 Parallel Scavenge 收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量如果对于收集器运作不太了解的话可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。 优点 缺点 使用场景
Parallel 收集器执行过程
4、CMS 收集器「-XX:UseConcMarkSweepGC(old)」 新生代采用复制算法老年代采用标记-整理算法 CMSConcurrent Mark Sweep以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用它是 HotSpot 虚拟机第一款真正意义上的并发收集器它第一次实现了让垃圾收集线程与用户线程基本上同时工作。 CMS 收集器执行过程 初始标记-》并发标记-》重新标记-》并发清理-》并发重置 其中只有『初始标记』不能和用户线程并发其他的四个是可以的。 优点 并发收集、低停顿吞吐量高 缺点 对 CPU 资源敏感会和服务抢资源无法处理浮动垃圾即在并发清理阶段又产生垃圾这种浮动垃圾只能等到下一次 GC 再清理了它使用的回收算法“标记-清除”算法会导致收集结束时会有大量空间碎片产生当然通过参数 -XX:UseCMSCompactAtFullCollection 可以让 jvm 在执行完标记清除后再做整理执行过程中的不确定性会存在上一次垃圾回收还没执行完然后垃圾回收又被触发的情况特别是在并发标记和并发清理阶段会出现一边回收系统一边运行也许没回收完就再次触发 full gc也就是“concurrent mode failure”此时会进入 stop the world使用 serial old 垃圾收集器来回收 使用场景 注重用户体验的系统低延时。 何为[ concurrent mode failure 错误
5、G1 收集器「-XX:UseG1GC」 一款面向服务器的垃圾收集器主要针对配备多颗处理器及大容量内存的机器以极高概率满足 GC 停顿时间要求的同时还具备高吞吐量性能特征。会预测的停顿的时间以及一些抉择比如 200ms 回收 10MB 和 50ms 回收 20MB 两种选择会选择第二种用以达到有限时间内最大的回收效率 优点 缺点 使用场景
逃逸分析
public void test() {Person person new Person();
}
上面的代码会经历如下几个步骤 加载 Person.class 到内存上在栈中开辟一段空间用于 test 方法的入栈然后在 test 方法的栈空间分配一个变量 p在堆内存中创建一块区域空间分配内存地址「new 关键字的作用」对空间的属性空间分配默认初始化构造函数初始化将分配的地址赋值给变量 p即 p 指向了刚刚划分并且初始化好的堆地址 按照上面的步骤每个对象的分配对象会直接分配在堆上但如果需要分配的对象非常多并且生命周期都比较短比如在某个循环中一直 new 某一个类的对象并且创建的对象不会作为返回值『或者是返回值的一部分 』返回给调用者那么这些数量多且生命周期短的对象将会占用较多的堆空间这些被占用的会由 GC 定时去清理但如果有一种手段尽量的让这些对象都存储在栈里面也就是方法栈这些对象的销毁会随着方法的出栈而消亡就不再需要 GC 去耗费宝贵的时间和资源去回收堆内存了STW 的时间自然也会短GC 的次数也会少这种手段就是逃逸分析在 JDK8 中逃逸分析是默认开启。
『但一种手段的出现肯定是有利也有弊开启逃逸分析也会耗费时间和资源就需要我们自己去测试分析手上的项目是否合适不能保证逃逸分析的性能收益必定高于它的消耗』
逃逸分析的分类 方法逃逸线程逃逸 方法逃逸
当一个对象在方法里面被定义后它可能被外部方法所引用例如作为调用参数传递到其它方法中。
线程逃逸
这个对象甚至可能被其它线程访问到例如赋值给类变量或可以在其它线程中访问的实例变量。
逃逸分析总结
当一个对象在其生命周期内被其他对象所持有那么就会发生逃逸。
最后祝大家早日学有所成拿到满意offer