jvm整理

    xiaoxiao2021-11-19  61

    ==========================================

    java编译执行过程

    jvm:字节码解释器 编译:将.java编译成java字节码(.class文件) 类加载:加载到内存并校验、解析和初始化 执行:根据不同平台解释成不同的机器码执行 保证跨平台

    编译

    一个现代编译器的主要工作流程如下: 源程序(source code)→预处理器(preprocessor)→编译器(compiler)→汇编程序(assembler)→目标程序(object code)→连接器(链接器,Linker)→可执行程序(executables]) 翻译方式一般分为编译和解释两种。 编译型:编译成汇编或机器语言直接执行 解释型:代码一行行的解释执行 jvm引入JIT编译,工作原理:当JIT编译启用时(默认是启用的),JVM读入.class文件解释后,将其发给JIT编译器。JIT编译器将字节码编译成本机机器代码。 通常javac将程序源代码编译转换成Java字节码,JVM通过解释字节码将其翻译成对应的机器指令,逐条读入,逐条解释翻译。很显然,经过解释执行,其执行速度必然会比可执行的二进制程序慢。为了提高执行速度,引入了JIT技术。在运行时JIT会把翻译过的机器码保存起来,以备下次使用。

    类加载

    类加载器有四个(引导类加载器由c++编写,其他由java编写) 1.引导类加载器 2.扩展类加载器 3.jar包类加载器 4.app类加载器 加载机制:双亲委托机制 首先将加载任务委托给父类加载器,一次追溯,直到最高父辈,如果父类加载器可以完成类加载任务,就成功返回;只要父类加载器无法完成次加载任务时,才自己加载。 类加载不会在一开始就把所有的类全部加载,而且需要的时候才去加载,并只会加载一次 类加载的过程 1.加载 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口。 2.连接 将java类的二进制代码合并到jvm的运行状态之中的过程 (1)验证 确保符合jvm规范,安全 (2)准备 在方法区中分为静态变量分配内存和初始值 (3)解析 虚拟机常量池内的符号引用替换为直接引用的过程。(比如String s =”aaa”,转化为 s的地址指向“aaa”的地址) 3.初始化 执行静态语句块 什么时候类初始化? 类加载分为类主动引用和类被动引用,主动引用一定会发生初始化,被动引用不会发生类的初始化 主动引用 –new一个类的对象 –调用类的静态成员(除了final常量)和静态方法 –使用java.lang.reflect包的方法对类进行反射调用 –当初始化一个类,如果其父类没有被初始化,则先初始化他的父类 –当要执行某个程序时,一定先启动main方法所在的类 被动引用 –当访问一个静态变量时,只有真正声明这个静态变量的类才会被初始化(通过子类引用父类的静态变量,不会导致子类初始化) –通过数组定义类应用,不会触发此类的初始化 A[] a = new A[10]; –引用常量(final类型)不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)

    jvm内存区域划分

    方法区:永久代,特殊的堆,存放类的运行时数据(静态变量,静态方法,常量池,代码),每一个类的运行时数据对应一个堆中的class对象 堆:jvm启动时创建,存放数组和对象实例 堆大小=新生代+年老代 新生代=eden+2*Survivor ,eden总是存放新生的对象,Survivor Spaces存放young gc后存活下来的对象,Old存放应用程序中生命周期长的内存对象(多次young gc后还存活的对象)或当新生代区域满时的对象 栈:栈由栈帧组成,栈帧由局部变量和操作数栈(存放计算的中间变量)组成,一个线程对应一个栈,调用方法入栈,方法结束出栈

    jvm 垃圾回收

    怎么判断对象是否存活?

    gc算法

    1.引用计数法(已被淘汰的算法) 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。 2.标记清楚法(一般full gc使用) 通过遍历GC Roots树判断可达性然后标记,未被标记的清楚 3.复制删除法(一般young gc使用) 从GC Root开始遍历每一个关联的活跃对象,将活跃对象复制到Survivor,然后清除所有eden对象 gc roots包含: 虚拟机栈(栈帧中的本地变量表)中引用的对象。 方法区中类静态属性引用的对象。 方法区中常量引用的对象。 本地方法栈中JNI(即一般说的Native方法)引用的对象。

    何时触发gc

    当eden满时触发1级gc 即young gc 当old满时触发0级gc 即full gc

    gc收集器类型

    串行收集器 -XX:+UseSerialGC:策略为年轻代串行复制,年老代串行标记整理 并行收集器 -XX:+UseParallelGC JDK5 -server的默认值 年轻代:暂停应用程序,多个垃圾收集线程并行的复制收集,线程数默认为CPU个数,CPU很多时,可用-XX:ParallelGCThreads= 设定线程数。 年老代:暂停应用程序,与串行收集器一样,单垃圾收集线程标记整理。 并发收集器 -XX:+UseConcMarkSweepGC 年轻代:同样是暂停应用程序,多个垃圾收集线程并行的复制收集。 年老代:则只有两次短暂停,其他时间应用程序与收集线程并发的清除。 增量并发收集器 在并发标记、清理的时候让GC线程、用户线程交叉运行,尽量减少GC线程的全程独占式执行

    **增量式gc 当GC进程运行时,应用程序停止运行。因此,当GC运行时间较长时,用户能够感到Java程序的停顿 增量式GC就是通过一定的回收算法,把一个长时间的中断,划分为很多个小的中断,通过这种方式减少GC对用户程序的影响 **被GC判断为”垃圾”的对象一定会回收吗 当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”(即意味着直接回收)。任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行,直接回收 **gc 并行与并发 并行:指多条垃圾收集线程并行,此时用户线程是没有运行的; 并发:指用户线程与垃圾收集线程并发执行,程序在继续运行,而垃圾收集程序运行于另一个个CPU上

    jvm调优

    调优主要的目的是减少minor GC的频率和Full GC的次数,通过控制堆内存的各个部分的比例和GC策略 full GC:对整个堆进行整理,导致Full GC一般由于以下几种情况: 1.旧生代空间不足 调优时尽量让对象在新生代GC时被回收、让对象在新生代多存活一段时间和不要创建过大的对象及数组避免直接在旧生代创建对象 2.Pemanet Generation空间不足 增大Perm Gen空间,避免太多静态对象 3.统计得到的GC后晋升到旧生代的平均大小大于旧生代剩余空间 控制好新生代和旧生代的比例 4.System.gc()被显示调用 垃圾回收不要手动触发,尽量依靠JVM自身的机制

    调优参数分为性能参数,行为参数,调试参数

    性能参数

    -xmn:新生代的大小 (1.4之后 之前是-XX:NewSize,-XX:MaxNewSize 设置为同一个值,这样能够防止在每次GC之后都要调整堆的大小,即抖动) -xms:初始堆的大小,默认情况下(-XX:MinHeapFreeRatio=40),当堆中可用内存小于40%,会增加到-xmx -xmx:堆的最大值,默认情况下(-XX: MaxHeapFreeRatio=70),当堆中可用内存大于70%,会增加到-xms -XX:SurvivorRatio=8 :Eden:Survivor1:Survivor2=8:1:1,整个堆的大小=年轻代大小+年老代大小,堆的大小不包含持久代大小,官方默认配置:年老代大小/年轻代大小=2/1左右(-XX:NewRatio=2 年老代/年轻代=2/1) -Xss:线程的栈内存(默认1M)相似参数-XX:ThreadStackSize -Xloggc:file:与-verbose:gc功能类似,只是将每次GC事件的相关情况记录到一个文件中,文件的位置最好在本地,以避免网络的潜在问题,若与verbose命令同时出现在命令行中,则以-Xloggc为准。 -XX:MaxPermSize=64m:方法区所能占用的最大内存(非堆内存) -XX:PermSize=64m:方法区分配的初始内存 -XX:MaxTenuringThreshold=15:对象在新生代存活区切换的次数(坚持过MinorGC的次数,每坚持过一次,该值就增加1),大于该值会进入老年代

    行为参数

    -XX:-UseSerialGC 启用串行GC,即采用Serial+Serial Old模式 -XX:-UseParallelGC 启用并行GC,即采用Parallel Scavenge+Serial Old收集器组合(-Server模式下的默认组合) -XX:GCTimeRatio=99 设置用户执行时间占总时间的比例(默认值99,即1%的时间用于GC) -XX:MaxGCPauseMillis=time 设置GC的最大停顿时间(这个参数只对Parallel Scavenge有效) -XX:+UseParNewGC 使用ParNew+Serial Old收集器组合 -XX:ParallelGCThreads 设置执行内存回收的线程数,在+UseParNewGC的情况下使用 -XX:+UseParallelOldGC 使用Parallel Scavenge +Parallel Old组合收集器 -XX:+UseConcMarkSweepGC 使用ParNew+CMS+Serial Old组合并发收集,优先使用ParNew+CMS,当用户线程内存不足时,采用备用方案Serial Old收集。 -XX:-DisableExplicitGC 禁止调用System.gc();但jvm的gc仍然有效 -XX:+ScavengeBeforeFullGC 新生代GC优先于Full GC执行

    调试参数

    -XX:-CITime 打印消耗在JIT编译的时间 -XX:ErrorFile=./hs_err_pid.log 保存错误日志或者数据到文件中 -XX:-ExtendedDTraceProbes 开启solaris特有的dtrace探针 -XX:HeapDumpPath=./java_pid.hprof 指定导出堆信息时的路径或文件名 -XX:-HeapDumpOnOutOfMemoryError 当首次遭遇OOM时导出此时堆中相关信息 -XX:OnError=”;” 出现致命ERROR之后运行自定义命令 -XX:OnOutOfMemoryError=”;” 当首次遭遇OOM时执行自定义命令 -XX:-PrintClassHistogram 遇到Ctrl-Break后打印类实例的柱状信息,与jmap -histo功能相同 -XX:-PrintConcurrentLocks 遇到Ctrl-Break后打印并发锁的相关信息,与jstack -l功能相同 -XX:-PrintCommandLineFlags 打印在命令行中出现过的标记 -XX:-PrintCompilation 当一个方法被编译时打印相关信息 -XX:-PrintGC 每次GC时打印相关信息 -XX:-PrintGC Details 每次GC时打印详细信息 -XX:-PrintGCTimeStamps 打印每次GC的时间戳 -XX:-TraceClassLoading 跟踪类的加载信息 -XX:-TraceClassLoadingPreorder 跟踪被引用到的所有类的加载信息 -XX:-TraceClassResolution 跟踪常量池 -XX:-TraceClassUnloading 跟踪类的卸载信息 -XX:-TraceLoaderConstraints 跟踪类加载器约束的相关信息

    建议

    1.-XX:PermSize尽量比-XX:MaxPermSize小,-XX:MaxPermSize>= 2 * -XX:PermSize, -XX:PermSize> 64m,一般对于4G内存的机器,-XX:MaxPermSize不会超过256m

    2.-Xms = -Xmx(线上Server模式),以防止抖动,大小受操作系统和内存大小限制,如果是32位系统,则一般-Xms设置为1g-2g(假设有4g内存),在64位系统上,没有限制,不过一般为机器最大内存的一半左右;

    3.-Xmn,在开发环境下,可以用-XX:NewSize和-XX:MaxNewSize来设置新生代的大小(-XX:NewSize<=-XX:MaxNewSize),在生产环境,建议只设置-Xmn,一般-Xmn的大小是-Xms的1/2左右,不要设置的过大或过小,过大导致老年代变小,频繁Full GC,过小导致minor GC频繁。如果不设置-Xmn,可以采用-XX:NewRatio=2来设置,也是一样的效果;

    4.-Xss一般是不需要改的,默认值即可。

    5.-XX:SurvivorRatio一般设置8-10左右,推荐设置为10,也即:Survivor区的大小是Eden区的1/10,一般来说,普通的Java程序应用,一次minorGC后,至少98%-99%的对象,都会消亡,所以,survivor区设置为Eden区的1/10左右,能使Survivor区容纳下10-20次的minor GC才满,然后再进入老年代,这个与 -XX:MaxTenuringThreshold的默认值15次也相匹配的。如果XX:SurvivorRatio设置的太小,会导致本来能通过minor回收掉的对象提前进入老年代,产生不必要的full gc;如果XX:SurvivorRatio设置的太大,会导致Eden区相应的被压缩。

    6.-XX:MaxTenuringThreshold默认为15,也就是说,经过15次Survivor轮换(即15次minor GC),就进入老年代, 如果设置的小的话,则年轻代对象在survivor中存活的时间减小,提前进入年老代,对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活时间,增加在年轻代即被回收的概率。需要注意的是,设置了 -XX:MaxTenuringThreshold,并不代表着,对象一定在年轻代存活15次才被晋升进入老年代,它只是一个最大值,事实上,存在一个动态计算机制,计算每次晋入老年代的阈值,取阈值和MaxTenuringThreshold中较小的一个为准。

    7.-XX:PretenureSizeThreshold一般采用默认值即可。

    原则:

    1.多数的Java应用不需要在服务器上进行GC优化;

    2.多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;

    3.在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合);

    4.减少创建对象的数量,减少使用全局变量和大对象;

    5.GC优化是到最后不得已才采用的手段,在实际使用中,分析GC情况优化代码比优化GC参数要多得多;

    GC优化目的有两个

    1.将转移到老年代的对象数量降低到最小; 2.减少full GC的执行时间; (1)减少使用全局变量和大对象; (2)调整新生代的大小到最合适; (3)设置老年代的大小为最合适; (4)选择合适的GC收集器;

    监控和调优的一般步骤为:

    1,监控GC的状态 使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化; 2,分析结果,判断是否需要优化 如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3秒,或者频繁GC,则必须优化;

    注:如果满足下面的指标,则一般不需要进行GC: Minor GC执行时间不到50ms; Minor GC执行不频繁,约10秒一次; Full GC执行时间不到1s; Full GC执行频率不算频繁,不低于10分钟1次;

    jvm监控

    参考 http://www.cricode.com/3132.html http://www.cnblogs.com/zhguang/p/3154584.html http://www.cnblogs.com/zhguang/p/3257367.html http://www.cnblogs.com/zhguang/p/3342039.html http://www.360doc.com/content/13/0305/10/15643_269388816.shtml http://www.open-open.com/lib/view/open1390916852007.html http://www.360doc.com/content/14/0218/23/9440338_353675002.shtml http://www.360doc.com/content/12/0630/08/6828497_221295060.shtml

    转载请注明原文地址: https://ju.6miu.com/read-678361.html

    最新回复(0)