当前位置: 首页 > 科技观察

jvm系列(八):jvm知识点概览

时间:2023-03-16 13:34:37 科技观察

江湖修炼绝世武功,必须内外兼修,招式精湛,内功深厚。武术的根本是内功。对于武功低的人(如江南七怪),招式更重要,因为不能靠内功直接伤人,只能靠招式取胜,但经过他们成为高手,内功就更重要了。内功低的人,即使招式再妙,也敌不过内功高的人。比如说,你的剑法再厉害,你一剑刺来,人家一掌断了你的剑,你的剑法怎么用?受伤了,你怎么打。同样,两者相辅相成。内功深厚之后,原本普通招式的威力就会成倍增加。我们从事发展的人也是如此。现在流行的框架越来越多,包装也越来越完善。各种框架无所不能,基本不用关注底层实现。初级程序员只要熟悉基本的使用方法,就可以快速开发上线;但对于资深程序员来说,内功的修养越来越重要,比如算法、设计模式、底层原理等,因此当出现问题时,可以快速定位到问题的本质。对于Java程序员来说,spring全家桶几乎可以搞定一切。spring全家桶是精妙之举,jvm是很重要的内功。如果线上出现性能问题,jvm调优是一个无法回避的问题。因此,JVM基础知识对于高级程序员的重要性不用多说。我们公司在面试高级开发人员的时候,jvm相关知识一定也是考核标准之一。本文将在之前写的jvm系列文章的基础上,整理出jvm需要注意的所有检查点。jvm整体梳理jvm系统一般分为四个部分:类加载机制jvm内存结构GC算法垃圾回收GC分析命令调优当然,这些知识点在之前的文章中都有详细介绍,这里我们只做主要的梳理这里创建了一张思维导图,展示了所有的知识点,因为图比较大,可以在公众号回复“jvm”查看。类加载机制的主要关注点:什么是类加载?类加载器双亲委派模型?什么是类加载?类加载是指将类的.class文件中的二进制数据读入内存。放在运行时数据区的方法区,然后在堆区创建java.lang.Class对象,将类的数据结构封装在方法区。类加载的最终产物是位于堆区的Class对象。Class对象封装了类在方法区的数据结构,为Java程序员提供了访问方法区数据结构的接口。类的生命周期类的生命周期包括这几个部分,加载、连接、初始化、使用和卸载。前三部分是类加载的过程,如下图;加载、查找和加载类的二进制数据,在Java堆中还创建了一个java.lang.Class类的对象连接,该连接包含三块内容:验证、准备和初始化。1)校验,文件格式、元数据、字节码、符号引用校验;2)准备工作,为类的静态变量分配内存,并初始化为默认值;3)解析,将类中的符号引用转换为直接引用初始化,将正确的初值赋给类的静态变量以供使用,在新的目标程序中使用卸载,进行垃圾回收。几个小问题?1.JVM初始化步骤?2.类初始化时机?3、有哪些情况?接下来,Java虚拟机会结束它的生命周期吗?答案参考这篇文章jvm系列(一):java类加载机制类加载器启动类加载器:BootstrapClassLoader,负责加载存放在JDK\jre\lib(JDK代表JDK的安装目录,下同),或者在-Xbootclasspath参数指定的路径下,虚拟机可以识别的类库扩展类加载器:ExtensionClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载DK\jre\lib\ext目录下或java.ext.dirs系统变量指定路径下的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。应用类加载器:ApplicationClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader实现,负责加载用户类路径(ClassPath)指定的类,开发者可以直接使用类加载器机制全面负责,当一个类加载器负责加载一个类,该类依赖的其他类和引用也将由类加载器加载,除非使用另一个类加载器来加载父类委托,让父类加载器尝试加载首先加载类,只有当父类加载器无法加载类时,才尝试从自己的类路径加载类。缓存机制将确保所有加载的类都将被缓存。当程序中需要使用一个类时,类加载器首先从缓存区中查找该类。只有当缓存区不存在时,系统才会读取该类对应的二进制数据,转换成Class对象,存入缓存中。区。这就是为什么修改Class之后,必须重启JVM,程序修改才会生效。jvm内存结构主要关注的是:jvm内存结构有哪些?java堆栈、本地方法堆栈和程序计数器是运行线程私有的内存区域。Java堆(Heap)是Java虚拟机管理的最大的一块内存。Java堆是虚拟机启动时创建的所有线程共享的内存区域。这块内存区的唯一用途就是存放对象实例,几乎所有的对象实例都在这里分配内存。方法区(MethodArea)和Java堆一样,是各个线程共享的一块内存区域。用于存放类信息,常量,静态变量,以及虚拟机编译的JIT。代码和其他数据。程序计数器(ProgramCounterRegister),程序计数器寄存器(ProgramCounterRegister)是一块很小的内存空间,它的作用可以看作是当前线程执行的字节码的行号指示器。JVM栈(JVMStacks)和程序计数器一样,Java虚拟机栈(JavaVirtualMachineStacks)也是线程私有的,它们的生命周期和线程一样。虚拟机栈描述了Java方法执行的内存模型:每个方法执行时,同时创建一个栈帧(StackFrame),用于存放局部变量表、操作栈、动态链接、方法出口等信息.每个方法被调用到执行完成的过程对应着虚拟机栈中一个栈帧从入栈到出栈的过程。原生方法栈(NativeMethodStacks),原生方法栈(NativeMethodStacks)和虚拟机栈很相似,不同的是虚拟机栈执行的是为虚拟机服务的Java方法(也就是字节码),而本地方法栈服务于虚拟机使用的本地方法。对象分配规则对象首先分配在伊甸区。如果Eden区没有足够的空间,虚拟机就会执行一次MinorGC。大对象直接进入老年代(大对象是指需要大量连续内存空间的对象)。这样做的目的是为了避免在Eden区和两个Survivor区之间进行大量的内存拷贝(新生代使用拷贝算法收集内存)。长寿命对象进入老年代。虚拟机为每个对象定义一个年龄计数器。如果对象经过一次MinorGC,对象就会进入Survivor区。之后,对象每经过一次MinorGC,年龄就会加1。直到达到阈值,对象才会进入老年区。动态判断对象的年龄。如果Survivor区所有同龄对象的大小之和大于Survivor空间的一半,则年龄大于等于这个年龄的对象可以直接进入老年代。空间分配保证。每进行一次MinorGC,JVM都会计算从Survivor区移动到old区的对象的平均大小。如果这个值大于old区剩余值的大小,就会进行FullGC。如果它小于该值,请检查HandlePromotionFailure设置。如果为true,则只执行Monitor。GC,如果为false,执行FullGC。如何通过参数控制各个内存区域参考这篇文章:jvm系列(二):JVM内存结构GC算法垃圾回收主要关注点:对象存活判断GC算法垃圾收集器对象存活判断判断一个对象是否存在一般有两种方式alive:引用计数:每个对象都有一个引用计数属性。添加引用时,count加1,释放引用时,count减1,count为0时,可以回收。这种方法比较简单,不能解决对象间循环引用的问题。ReachabilityAnalysis:从GCRoots开始向下搜索,搜索所经过的路径称为引用链。当一个对象没有任何引用链连接到GCRoots时,证明该对象不可用且不可达。GC算法GC有三种基本算法:标记-清除算法、复制算法和标记-压缩算法。我们常用的垃圾收集器一般都是采用分代收集算法。Mark-Sweep算法,“标记-清除”(Mark-Sweep)算法,顾名思义,该算法分为“标记”和“清除”两个阶段:先标记所有需要回收的对象,之后标记完成统一收集所有标记的对象。复制算法,即“Copying”的收集算法,将可用内存按容量大小分成两块,每次只使用其中一块。当这块内存用完后,将存活的对象复制到另一块中,然后一次性清理掉已使用的内存空间。mark-compression算法,标记过程还是和“mark-clear”算法一样,只是下一步不是直接清理可回收对象,而是将所有存活的对象移到一端,然后直接清理端边界外的内存分代收集算法,“分代收集”(GenerationalCollection)算法,将Java堆分为新生代和老年代,这样可以根据各个年龄段的特点采用最合适的收集算法.Garbagecollector串行收集器,串行收集器是最古老、最稳定和高效的收集器,可能会产生长时间的停顿,并且只使用一个线程进行回收。ParNew收集器,ParNew收集器实际上是Serial收集器的多线程版本。Parallel收集器,ParallelScavenge收集器类似于ParNew收集器,Parallel收集器更注重系统的吞吐量。ParallelOld收集器,ParallelOld是老版本的ParallelScavenge收集器,采用多线程和“标记排序”算法的CMS收集器,CMS(ConcurrentMarkSweep)收集器是一种旨在获得最短恢复停顿时间的收集器.G1收集器,G1(Garbage-First)是一个面向服务器的垃圾收集器,主要针对配备多处理器和大容量内存的机器。以极高的概率满足GC停顿时间要求,同时具有高吞吐量的性能特点GC算法和垃圾收集器算法图,更详细的内容参考:jvm系列(三):GC算法垃圾收集器GC分析命令调优主要关注点:GC日志分析调优命令调优工具GC日志分析摘录部分GC日志YoungGC恢复日志:2016-07-05T10:43:18.093+0800:25.395:[GC[PSYoungGen:274931K->10738K(274944K)]371093K->147186K(450048K),0.0668480secs][Times:user=0.17sys=0.08,real=0.07secs]FullGC恢复日志:2016-07-05T10:43:18.160+0800:25.462:[FullGC[PSYoungGen:10738K->0K(274944K)][ParOldGen:136447K->140379K(302592K)]147186K->140379K(577536K)[PSPermGen:85411K->85376K(171008K)],0.6763541secs][Times:y2s,real=0.750.68secs]根据上面的日志分析,PSYoungGen、ParOldGen和PSPermGen属于Parallel收集器。其中,PSYoungGen表示GC回收前后年轻代的内容。根据上面的日志分析,PSYoungGen、ParOldGen、PSPermGen属于Parallel收集器。其中,PSYoungGen表示gc回收前后年轻代的内存变化;ParOldGen表示gc回收前后老年代的内存变化;PSPermGen表示gc回收前后***区的内存变化。Younggc主要针对年轻代的内存回收,比较频繁,耗时较短;fullgc会返回整个堆内存,耗时较长,所以一般尽量减少fullgc的次数YoungGClogs:FullGClogs:JavaGC分析参考:jvm系列(五)JavaGC分析和调优命令SunJDK监控和排错命令包括jpsjstatjmapjhatjstackjinfojps,JVMProcessStatusTool,显示指定系统中所有HotSpot虚拟机进程。jstat,JVMstatisticsMonitoring是用来监控虚拟机运行时的状态信息的命令。可以显示虚拟机进程中的类加载、内存、垃圾回收、JIT编译等运行数据。jmap,JVMMemoryMap命令用于生成堆转储文件jhat,JVM堆分析工具命令与jmap结合使用,分析jmap生成的转储,jhat有一个内置的微型HTTP/HTML服务器,经过生成dump的分析结果,可以在浏览器中查看jstack,用于生成java虚拟机当前时刻的线程快照。jinfo,JVMConfigurationinfo该命令用于实时查看和调整虚拟机的运行参数。详细的命令使用可以参考这里:jvm系列(四):jvm调优-命令调优工具常用的调优工具分为两大类。),GChisto。jconsole,JavaMonitoringandManagementConsole是java5.0开始JDK自带的java监控管理控制台。它用于监控JVM中的内存、线程和类。jvisualvm,jdk自带的全能工具,可以分析内存快照,线程快照;监控内存变化、GC变化等。MAT,MemoryAnalyzerTool,基于Eclipse的内存分析工具,是一款快速且功能丰富的Java堆分析工具,可以帮助我们发现内存泄漏,降低内存消耗GChisto,专业分析gc日志的工具使用参考:jvm系列(七):jvmtuning-tools【本文为专栏作者《纯洁的微笑》原创稿件,转载请微信联系作者♂获取授权】点此查看作者更多好文