G1回收算法
parnew+cms痛点
stw(Stop The World) 导致系统停止工作
消耗cpu
系统的停顿影响时间是不可控的
所有的堆被分成很多的region区域
最多可以分配2048个region
年轻代和老年代都只是逻辑概念
老年代的region如果超过40%大概800个大小就会触发mixed GC 混合GC
可以手动指定回收的时间 -XX:MaxGCPauseMills单位毫秒
追踪每个对象的大小以及回收时间,尽量把系统的影响控制在我们设置的时间内
region不属于固定的年轻代和老年代,这个是G1回收器自动控制和调控
如果想使用G1回收器 加上参数-XX:+UseG1GC
region的大小是2的倍数也可以用 -XX:G1HeapRegionSize指定大小,最好不用设置,保持默认
G1中大对象的判断是超过了region的一半就认为是大对象<br>大对象是不会进入老年代的,还是用年轻代的承担
年轻代
初始化Eden区域是5%的大小 大概100region
也有eden区和survive区概念默认还是8:1:1<br>大概Eden占用1000个2个S200个左右
如果超过60%的region就会触发minorGC
采用复制算法
老年代
标记对象 系统处于停滞 还是非常快的
并发标记所有的GC ROOTS比较耗时,要追踪所有的存活对象,系统可以继续运行创建对象
混合回收,这个时候就会把停顿回收的时间控制在我们设定的时间之内,如果不能回收完成就继续让系统运行一段时间<br>在进行回收,至到回收完成,就是循环的进行回收又恢复运行,达到新生代的region在5%就停止了,默认是进行8次
-XX:MaxGCPauseMills
要根据线上系统合理的调节这个值,因为G1的垃圾回收是很智能的<br>他会尽量的根据你这个值来进行垃圾回收,而不是一定要到了新生代<br>的对象达到60%才回收,本身我们的系统就是要分配一个超大的内存<br>给应用程序,而我们设置的回收时间又特别久,这样回收也非常的慢<br>对系统的影响卡顿也非常的严重,导致系统卡顿很长时间,如果对系统<br>延迟有严格要求的就会非常的不好,所以我在设置这个值的时候要合理<br>评估系统的新生对象大小,多久可以到系统的60%,对系统的延迟可以<br>容忍到多久,尽量的设置一个比较优的值来作为回收卡顿时间,混合回收<br>也是会按照这个时间来进行回收。
其实G1调优的核心关键
还是要考虑我们存活的对象是否让S区域能够放下,不能直接进入老年代
合理的调整S区域的大小避免年龄动态规则让对象进入老年代
尽量让系统不要过多的进行系统GC,快速的进行回收<br>减小混合GC,因为混合回收还是比较慢的
region回收规则
默认是低于自身85%就才可以回收<br>回收成本就不会太高
-XX:G1MixedGCLiveThresholdPercent参数设置<br>默认是85
G1回收日志分析
0.904: [GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0046285 secs]<br> [Parallel Time: 3.0 ms, GC Workers: 4]<br> [GC Worker Start (ms): Min: 905.0, Avg: 905.1, Max: 905.3, Diff: 0.3]<br> [Ext Root Scanning (ms): Min: 1.1, Avg: 1.4, Max: 2.0, Diff: 0.9, Sum: 5.5]<br> [Update RS (ms): Min: 0.0, Avg: 0.4, Max: 1.7, Diff: 1.7, Sum: 1.7]<br> [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]<br> [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]<br> [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]<br> [Object Copy (ms): Min: 0.0, Avg: 0.9, Max: 1.6, Diff: 1.6, Sum: 3.7]<br> [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.2]<br> [Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 4]<br> [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]<br> [GC Worker Total (ms): Min: 2.6, Avg: 2.8, Max: 2.9, Diff: 0.3, Sum: 11.2]<br> [GC Worker End (ms): Min: 907.9, Avg: 907.9, Max: 907.9, Diff: 0.0]<br> [Code Root Fixup: 0.0 ms]<br> [Code Root Purge: 0.0 ms]<br> [Clear CT: 0.1 ms]<br> [Other: 1.6 ms]<br> [Choose CSet: 0.0 ms]<br> [Ref Proc: 0.1 ms]<br> [Ref Enq: 0.0 ms]<br> [Redirty Cards: 0.0 ms]<br> [Humongous Register: 0.1 ms]<br> [Humongous Reclaim: 0.8 ms]<br> [Free CSet: 0.0 ms]<br> [Eden: 3072.0K(51.0M)->0.0B(50.0M) Survivors: 0.0B->1024.0K Heap: 232.5M(1024.0M)->1800.1K(1024.0M)]<br> [Times: user=0.00 sys=0.00, real=0.01 secs] <br>分析<br>0.904: [GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0046285 secs]<br>0.904秒开始进行一次youngGC CPU耗时0.0046285秒 <br><br><br>
ParNew+CMS GC日志怎么看
Java HotSpot(TM) 64-Bit Server VM (25.161-b12) for windows-amd64 JRE (1.8.0_161-b12), built on Dec 19 2017 17:52:25 by "java_re" with MS VC++ 10.0 (VS2010)<br>Memory: 4k page, physical 8281908k(2603052k free), swap 11799044k(3966832k free)<br>CommandLine flags: -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:MaxNewSize=4194304 -XX:NewSize=4194304 -XX:OldPLABSize=16 -XX:PretenureSizeThreshold=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:SurvivorRatio=8 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC <br>这个是JVM启动的参数配置 初始化大小 新生代的大小 大对象的设置 是否大于GC时间 是否记录log日志<br><br>0.306: [GC (Allocation Failure) 0.306: [ParNew: 3115K->384K(3712K), 0.0037058 secs] 3115K->1767K(9856K), 0.0039580 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] <br>0.306开始进行GC回收,原因是无法再新生代进行创建对象 <br>[ParNew: 3115K->384K(3712K), 0.0037058 secs] 新生代可用大小 3.5M左右 存活的对象还有384k 回收时间大概3毫秒<br>3115K->1767K(9856K), 0.0039580 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 整个堆还有9M多,存活的一共1767k数据 <br><br><br>0.310: [GC (Allocation Failure) 0.310: [ParNew: 2494K->0K(3712K), 0.0017355 secs] 3878K->1763K(9856K), 0.0018016 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] <br>Heap<br> par new generation total 3712K, used 2114K [0x00000000ff600000, 0x00000000ffa00000, 0x00000000ffa00000)<br> eden space 3328K, 63% used [0x00000000ff600000, 0x00000000ff810bc0, 0x00000000ff940000)<br> from space 384K, 0% used [0x00000000ff940000, 0x00000000ff940000, 0x00000000ff9a0000)<br> to space 384K, 0% used [0x00000000ff9a0000, 0x00000000ff9a0000, 0x00000000ffa00000)<br> concurrent mark-sweep generation total 6144K, used 1763K [0x00000000ffa00000, 0x0000000100000000, 0x0000000100000000)<br> Metaspace used 3447K, capacity 4496K, committed 4864K, reserved 1056768K<br> class space used 376K, capacity 388K, committed 512K, reserved 1048576K<br>
JVM总结
指令计数器和虚拟机栈分别存放指令和数据的设计
方法执行完后, 栈帧立马被出栈, 那该栈帧中的变量等数据是立马就被回收
双亲委派模型设计的出发点很重要,对于任意一个类,都需要由加载它的类加载器和这个类本身一同确立其在Java虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。<br>也就是说,判断2个类是否“相等”,只有在这2个类是由同一个类加载器加载的前提下才有意义,否则即使这2个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,这2个类必定不相等。<br> 基于双亲委派模型设计,那么Java中基础的类,Object类似Object类重复多次的问题就不会存在了,因为经过层层传递,加载请求最终都会被Bootstrap ClassLoader所响应。加载的Object类也会只有一个,否则如果用户自己编写了一个java.lang.Object类,并把它放到了ClassPath中,会出现很多个Object类,这样Java类型体系中最最基础的行为都无法保证,应用程序也将一片混乱。
类加载
JVM加载器有哪些
根加载器(BootStrap ClassLoader)
扩展加载器(Extend ClassLoader)
类加载器(ClassPath ClassLoader)
自定义加载器
双亲委派
什么是双亲委派
委托自己的父类去加载,如果加载不了,自己加载,如果还是不能加载,子类加载
双亲委派的目的是什么
为了安全考虑,如果我们自己写了一个基础类覆盖了,这样就会有比较大的风险
避免重复加载 + 避免核心类篡改<br>采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java<br>API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。
加载一个类会做什么
初始阶段
对静态变量赋值
成员属性赋值
静态初始化
构造函数
tomcat的类加载器
打破双亲委派
tomcat会部署很多不同的应用程序,而且相互之间会用不同版本的jar,相互之间要隔离
部署在同一个web容器中相同的类库相同的版本可以共享。
web容器也有自己依赖的类库,不能于应用程序的类库混淆
web容器需要支持 jsp 修改后不用重启
加载器有哪些
common加载器
tomcat最基本的类加载器,加载路径中的class可以被tomcat和各个webapp访问
catalina加载器
tomcat私有的类加载器,webapp不能访问其加载路径下的class,即对webapp不可见
shared加载器
各个webapp共享的类加载器,对tomcat不可见
webapp加载器
webapp私有的类加载器,只对当前webapp可见
jsp加载器
每个jasper类加载器加载一个jsp文件
JVM内存区域
方法区
存放class的区域,或者叫做metaspace,元数据空间<br>
java虚拟机栈
当线程执行到某个方法的时候,如果这个方法有局部变量,<br>那么就需要一块区域来存放局部变量的数据信息。这个区域就叫做java虚拟机栈。<br>
JVM垃圾回收
垃圾回收器是一个后台线程
当我们创建的对象再堆中无法存储的时候就会自动回收没有GC ROOTS引用的对象
我们创建的对象优先分配在新生代对象中
当新生代的空间快满的时候就触发垃圾回收机制
回收时通过可达性算法追踪是否有引用,如果没有就进行回收
JVM规范中认为方法的局部变量,静态变量引用的对象都是不可回收的这些都可以称为GC ROOTS
引用类型
强引用
就是普通的声明局部变量,静态变量
软引用
利用SoftReference<T>包装的对象都是软引用
一般新生代充足的情况是不会回收的,<br>但是如果空间不足的时候就会强制回收软引用对象<br>不管是否有引用
弱引用
利用WeakReference<T>引用
只要系统发生垃圾回收就会销毁对象
finalize()
重写Object的finalize()方法 利用静态变量引用自己也可以强制不用回收
这种的使用基本上很少,大概知道可以让没有GC ROOTS的引用也可以不用回收
根据JVM规范,方法局部变量和静态变量被称为GC ROOTS,如果有GC ROOTS是不可以回收的