JVM详解
JVM工作原理

JVM工作原理JVM(Java虚拟机)是Java程序的运行环境,它负责将Java源代码编译成可执行的字节码,并提供运行时环境来执行字节码。
JVM的工作原理涉及到类加载、内存管理、垃圾回收、即时编译等多个方面。
1. 类加载JVM通过类加载器(ClassLoader)来加载Java类。
类加载器根据类的全限定名(包括包名和类名)在类路径中查找对应的字节码文件,并将其加载到内存中。
类加载器采用双亲委派模型,即先由父类加载器尝试加载类,如果父类加载器无法加载,则由子类加载器尝试加载。
这种模型保证了类的唯一性和安全性。
2. 内存管理JVM将内存分为多个区域,包括方法区、堆、栈和程序计数器。
方法区存储类的元数据信息,如字段、方法、常量池等。
堆是存放对象实例的区域,通过垃圾回收机制来管理内存的分配和释放。
栈用于存储方法的局部变量和方法调用信息。
程序计数器用于指示当前线程执行的字节码指令。
3. 垃圾回收JVM通过垃圾回收机制自动回收不再使用的对象内存。
垃圾回收器会定期扫描堆内存,标记所有还在使用的对象,然后清理掉未被标记的对象。
常见的垃圾回收算法有标记-清除、复制、标记-整理等。
JVM还提供了不同的垃圾回收器,如Serial、Parallel、CMS、G1等,可以根据应用场景选择合适的垃圾回收器。
4. 即时编译JVM使用即时编译器(Just-In-Time Compiler)将热点代码(经常被执行的代码)编译成本地机器码,以提高执行效率。
JVM会监测程序的运行情况,根据热点代码的执行频率和调用关系进行优化编译。
即时编译器可以选择不同的编译策略,如解释执行、编译执行或混合执行。
5. 内存模型JVM定义了Java程序在多线程环境下的内存模型,保证多线程的内存可见性和有序性。
内存模型规定了线程之间如何进行通信和同步。
JVM使用主内存和工作内存的概念,线程之间的共享变量存储在主内存中,每个线程有自己的工作内存,线程对共享变量的操作先在工作内存中进行,然后通过主内存来同步和通信。
面试谈jvm原理

面试谈jvm原理Java虚拟机(JVM)是Java语言运行的基础。
JVM具有封装性、跨平台性、高度优化和可扩展性等特点,是Java应用程序的核心。
在Java的诞生初期,由于硬件环境和操作系统制约,JVM起到了垫底的作用。
而今天,JVM已经成为Java 运行效率和安全性的保障。
下面是一些我认为JVM原理面试时可能会涉及的重点:1. JVM的内存模型:JVM将内存分为堆内存和栈内存,堆内存用于存储对象实例和数组,而栈内存则用于存储方法的执行状态。
同时,JVM还有方法区和永久代的概念。
这些内存区域的大小和分配情况会影响JVM的性能和稳定性。
2. 垃圾回收机制:JVM的内存管理包括垃圾回收机制和内存分配机制。
垃圾回收机制是JVM实现自动内存管理的核心,JVM会周期性地扫描堆内存中没有被引用的对象,并自动回收它们所占用的内存。
垃圾回收机制常用的算法包括标记清除、复制和标记整理等。
3. 类加载机制:Java程序在运行时,需要将类文件中的二进制数据加载到JVM 中,才能执行相应的操作。
类加载机制将类文件加载到JVM中,并将它们解析为Java类。
类加载机制包括三个阶段:加载、链接和初始化。
4. JIT编译器:JIT(Just In Time)编译器是JVM在运行时动态优化的关键组件。
JIT编译器可以在程序运行时,根据代码的执行情况,生成本地机器代码,以提高程序的效率。
5. JVM调优:JVM的性能和稳定性很大程度上取决于JVM参数的设置和调整。
面试时,可能会涉及到如何根据系统的特点和需求,设置JVM参数以达到最佳性能和稳定性的问题。
总之,有关JVM原理的面试问题,往往涉及到JVM的内存模型、垃圾回收机制、类加载机制、JIT编译器和JVM调优等方面。
需要候选人对这些方面有比较深入的了解。
jvm原理

JVM 原理解释JVM 全称是 Java Virtual Machine ,Java 虚拟机,这个 JVM 你是看不到的,它存在内存中。
我们知道计算机的基本构成是:运算器、控制器、存储器、输入和输出设备,那这个 JVM 也是有这成套的元素,运算器是当然是交给硬件 CPU 还处理了,只是为了适应“一次编译,随处运行”的情况,需要做一个翻译动作,于是就用了JVM 自己的命令集,JVM 的命令集则是可以到处运行的,因为 JVM 做了翻译,根据不同的CPU ,翻译成不同的机器语言。
JVM 是一个内存中的虚拟机,那它的存储就是内存了,我们写的所有类、常量、变量、方法都在内存中。
JVM 的组成部分Class Loader 类加载器类加载器的作用是加载类文件(.class)到内存,Class Loader 加载的 class 文件是有格式要求的。
类加载的最终产品是位于运行时数据区的堆区的Class对象。
Class对象封装了类在方法区内部的数据结构。
并且向JAVA程序提供了访问类在方法区内的数据结构。
JVM加载class文件的原理机制1. Java 中的所有类,必须被装载到 JMV 中才能运行,这个装载工作是由 JVM 中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中。
2. Java中的类大致分为三种:a) 系统类b) 扩展类c) 由程序员自定义的类3. 类装载方式,有两种:a) 隐式装载,程序在运行过程中当碰到通过 new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中。
b) 显式装载,通过 class.forname() 等方法,显式加载需要的类。
4. 类加载的动态性体现一个应用程序总是由n多个类组成,Java 程序启动时,并不是一次把所有的类全部加载后再运行,它总是先把保证程序运行的基础类一次性加载到 JVM 中,其它类等到 JVM 用到的时候再加载,这样的好处是节省了内存的开销。
JVM年轻代,老年代,永久代详解

JVM年轻代,⽼年代,永久代详解前⾔最近被问到了这个问题,解释的不是很清晰,有⼀些概念略微模糊,在此进⾏整理和记录,分享给⼤家。
本篇⽂章主要讲解内存区域的年轻代,⽼年代和永久代,略微提及⼀些垃圾回收算法,下⾯是正⽂。
堆整体堆主要⽤于存放各种类的实例对象和数组。
在java中被分为两个区域:年轻代和⽼年代。
在java中还有⼀个永久代的意思,这⾥最后会单独说明。
年轻代和⽼年代的划分是为了更好的内存分派及回收。
提⾼效率。
堆是垃圾回收机制的重点区域。
我们知道垃圾回收机制有三种,minor gc,major gc 和full gc。
针对于堆的就是前两种。
年轻代的叫 minor gc,⽼年代的叫major gc。
年轻代年轻代⽤来存放新近创建的对象,尺⼨随堆⼤⼩的增加和减少⽽相应的变化,默认值是保持为堆的1/15。
年轻代的⼤⼩可以通过-xmn设置固定⼤⼩,也可以通过-xx:newratio设置年轻代和年⽼代的⽐例。
年轻代中存在的对象是死亡⾮常快的。
存在朝⽣⼣死的情况。
所以为了提⾼年轻代的垃圾回收效率,⼜将年轻代划分为三个区域,⼀个eden和两个sunrvivor from。
eden和survivor默认⽐例是8:1:1,进⾏垃圾回收采⽤的是分代复制算法。
每次新⽣代的使⽤,会是eden区和⼀块survivor区。
当我们进⾏垃圾回收的时候,清除正在使⽤的区域,将其中的存货对象,放⼊到另⼀个survivor区域,并进⾏整理,保证空间的连续。
如果对象长时间存活,则将对象移动到⽼年区。
存活下来的对象,他的年龄会增长1。
当对象的年龄⼀次次存活,⼀次次增长,到达15的时候,这些对象就会移步到⽼年代。
在年轻代执⾏gc的时候,如果⽼年代的连续空间⼩于新⽣代对象的总⼤⼩,就会触发⼀次full gc。
是为了给新⽣代做担保,保证新⽣代的⽼年对象可以顺利的进⼊到⽼年代的内存区。
⽼年代⽼年代中存放的对象是存活了很久的,年龄⼤于15的对象。
JVM参数配置及详解-Xms-Xmx-Xmn-Xss调优总结

JVM参数配置及详解-Xms-Xmx-Xmn-Xss调优总结堆⼤⼩设置JVM 中最⼤堆⼤⼩有三⽅⾯限制: ①、相关操作系统的数据模型(32-bt还是64-bit)限制; ②、系统的可⽤虚拟内存限制; ③、系统的可⽤物理内存限制. 32位系统下,⼀般限制在1.5G~2G;64为操作系统对内存⽆限制.在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最⼤可设置为1478m. 堆内存逻辑上分为三部分:新⽣区+养⽼区+永久代 物理上分为两部分:新⽣+养⽼ Jdk1.7之前,有永久代,但已经逐步“去永久代” Jdk1.8之后,⽆永久代,由元空间替代典型设置:java -Xmx3550m -Xms3550m -Xmn2g -Xss128k①、-Xmx3550m:设置JVM最⼤可⽤内存为3550M.②、-Xms3550m:设置JVM促使内存为3550m.此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存.③、-Xmn2g:设置年轻代⼤⼩为2G.整个堆⼤⼩=年轻代⼤⼩ + 年⽼代⼤⼩ + 持久代⼤⼩.持久代⼀般固定⼤⼩为64m,所以增⼤年轻代后,将会减⼩年⽼代⼤⼩.此值对系统性能影响较⼤,Sun官⽅推荐配置为整个堆的3/8.④、-Xss128k:设置每个线程的堆栈⼤⼩.JDK5.0以后每个线程堆栈⼤⼩为1M,以前每个线程堆栈⼤⼩为256K.更具应⽤的线程所需内存⼤⼩进⾏调整.在相同物理内存下,减⼩这个值能⽣成更多的线程.但是操作系统对⼀个进程内的线程数还是有限制的,不能⽆限⽣成,经验值在3000~5000左右.java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0①、-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年⽼代的⽐值(除去持久代).设置为4,则年轻代与年⽼代所占⽐值为1:4,年轻代占整个堆栈的1/5②、-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的⼤⼩⽐值.设置为4,则两个Survivor区与⼀个Eden区的⽐值为2:4,⼀个Survivor 区占整个年轻代的1/6③、-XX:MaxPermSize=16m:设置持久代⼤⼩为16m.④、-XX:MaxTenuringThreshold=0:设置垃圾最⼤年龄.如果设置为0的话,则年轻代对象不经过Survivor区,直接进⼊年⽼代. 对于年⽼代⽐较多的应⽤,可以提⾼效率.如果将此值设置为⼀个较⼤值,则年轻代对象会在Survivor区进⾏多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论.回收器选择JVM给了三种选择:串⾏收集器,并⾏收集器,并发收集器,但是串⾏收集器只适⽤于⼩数据量的情况,所以这⾥的选择主要针对并⾏收集器和并发收集器.默认情况下,JDK5.0以前都是使⽤串⾏收集器,如果想使⽤其他收集器需要在启动时加⼊相应参数.JDK5.0以后,JVM会根据当前系统配置进⾏判断.吞吐量优先的并⾏收集器如上⽂所述,并⾏收集器主要以到达⼀定的吞吐量为⽬标,适⽤于科学技术和后台处理等.典型配置:java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20①、-XX:+UseParallelGC:选择垃圾收集器为并⾏收集器.此配置仅对年轻代有效.即上述配置下,年轻代使⽤并发收集,⽽年⽼代仍旧使⽤串⾏收集.②、-XX:ParallelGCThreads=20:配置并⾏收集器的线程数,即:同时多少个线程⼀起进⾏垃圾回收.此值最好配置与处理器数⽬相等.java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC①、-XX:+UseParallelOldGC:配置年⽼代垃圾收集⽅式为并⾏收集.JDK6.0⽀持对年⽼代并⾏收集.java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100①、-XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间,如果⽆法满⾜此时间,JVM会⾃动调整年轻代⼤⼩,以满⾜此值.java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy①、-XX:+UseAdaptiveSizePolicy:设置此选项后,并⾏收集器会⾃动选择年轻代区⼤⼩和相应的Survivor区⽐例,以达到⽬标系统规定的最低相应时间或者收集频率等,此值建议使⽤并⾏收集器时,⼀直打开.响应时间优先的并发收集器如上⽂所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间.适⽤于应⽤服务器,电信领域等.典型配置:java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC①、-XX:+UseConcMarkSweepGC:设置年⽼代为并发收集.测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明.所以,此时年轻代⼤⼩最好⽤-Xmn设置.②、-XX:+UseParNewGC:设置年轻代为并⾏收集.可与CMS收集同时使⽤.JDK5.0以上,JVM会根据系统配置⾃⾏设置,所以⽆需再设置此值.java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection①、-XX:CMSFullGCsBeforeCompaction=5:由于并发收集器不对内存空间进⾏压缩,整理,所以运⾏⼀段时间以后会产⽣"碎⽚",使得运⾏效率降低.此值设置运⾏多少次GC以后对内存空间进⾏压缩,整理.②、-XX:+UseCMSCompactAtFullCollection:打开对年⽼代的压缩.可能会影响性能,但是可以消除碎⽚辅助信息JVM提供了⼤量命令⾏参数,打印信息,供调试使⽤.主要有以下⼀些:-XX:+PrintGC输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs][Full GC 121376K->10414K(130112K), 0.0650971 secs]-XX:+PrintGCDetails输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs][GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上⾯两个混合使⽤输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执⾏时间.可与上⾯混合使⽤输出形式:Application time: 0.5291524 seconds-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间.可与上⾯混合使⽤输出形式:Total time for which application threads were stopped: 0.0468229 seconds-XX:PrintHeapAtGC:打印GC前后的详细堆栈信息输出形式:34.702: [GC {Heap before gc invocations=7:def new generation total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)eden space 49152K, 99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)from space 6144K, 55% used [0x221d0000, 0x22527e10, 0x227d0000)to space 6144K, 0% used [0x21bd0000, 0x21bd0000, 0x221d0000)tenured generation total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)the space 69632K, 3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:def new generation total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)eden space 49152K, 0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)from space 6144K, 55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)to space 6144K, 0% used [0x221d0000, 0x221d0000, 0x227d0000)tenured generation total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)the space 69632K, 4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)}, 0.0757599 secs]-Xloggc:filename:与上⾯⼏个配合使⽤,把相关⽇志信息记录到⽂件以便分析.常见配置汇总堆设置-Xms:初始堆⼤⼩-Xmx:最⼤堆⼤⼩-XX:NewSize=n:设置年轻代⼤⼩-XX:NewRatio=n:设置年轻代和年⽼代的⽐值.如:为3,表⽰年轻代与年⽼代⽐值为1:3,年轻代占整个年轻代年⽼代和的1/4-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的⽐值.注意Survivor区有两个.如:3,表⽰Eden:Survivor=3:2,⼀个Survivor区占整个年轻代的1/5-XX:MaxPermSize=n:设置持久代⼤⼩收集器设置-XX:+UseSerialGC:设置串⾏收集器-XX:+UseParallelGC:设置并⾏收集器-XX:+UseParalledlOldGC:设置并⾏年⽼代收集器-XX:+UseConcMarkSweepGC:设置并发收集器垃圾回收统计信息-XX:+PrintGC-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-Xloggc:filename并⾏收集器设置-XX:ParallelGCThreads=n:设置并⾏收集器收集时使⽤的CPU数.并⾏收集线程数.-XX:MaxGCPauseMillis=n:设置并⾏收集最⼤暂停时间-XX:GCTimeRatio=n:设置垃圾回收时间占程序运⾏时间的百分⽐.公式为1/(1+n)并发收集器设置-XX:+CMSIncrementalMode:设置为增量模式.适⽤于单CPU情况.-XX:ParallelGCThreads=n:设置并发收集器年轻代收集⽅式为并⾏收集时,使⽤的CPU数.并⾏收集线程数.调优总结年轻代⼤⼩选择响应时间优先的应⽤:尽可能设⼤,直到接近系统的最低响应时间限制(根据实际情况选择).在此种情况下,年轻代收集发⽣的频率也是最⼩的.同时,减少到达年⽼代的对象.吞吐量优先的应⽤:尽可能的设置⼤,可能到达Gbit的程度.因为对响应时间没有要求,垃圾收集可以并⾏进⾏,⼀般适合8CPU以上的应⽤.年⽼代⼤⼩选择响应时间优先的应⽤:年⽼代使⽤并发收集器,所以其⼤⼩需要⼩⼼设置,⼀般要考虑并发会话率和会话持续时间等⼀些参数.如果堆设置⼩了,可以会造成内存碎⽚,⾼回收频率以及应⽤暂停⽽使⽤传统的标记清除⽅式;如果堆⼤了,则需要较长的收集时间.最优化的⽅案,⼀般需要参考以下数据获得:并发垃圾收集信息持久代并发收集次数传统GC信息花在年轻代和年⽼代回收上的时间⽐例减少年轻代和年⽼代花费的时间,⼀般会提⾼应⽤的效率吞吐量优先的应⽤:⼀般吞吐量优先的应⽤都有⼀个很⼤的年轻代和⼀个较⼩的年⽼代.原因是,这样可以尽可能回收掉⼤部分短期对象,减少中期的对象,⽽年⽼代尽存放长期存活对象.较⼩堆引起的碎⽚问题因为年⽼代的并发收集器使⽤标记,清除算法,所以不会对堆进⾏压缩.当收集器回收时,他会把相邻的空间进⾏合并,这样可以分配给较⼤的对象.但是,当堆空间较⼩时,运⾏⼀段时间以后,就会出现"碎⽚",如果并发收集器找不到⾜够的空间,那么并发收集器将会停⽌,然后使⽤传统的标记,清除⽅式进⾏回收.如果出现"碎⽚",可能需要进⾏如下配置:-XX:+UseCMSCompactAtFullCollection:使⽤并发收集器时,开启对年⽼代的压缩.-XX:CMSFullGCsBeforeCompaction=0:上⾯配置开启的情况下,这⾥设置多少次Full GC后,对年⽼代进⾏压缩在同⼀个⼯程下,有两个类,这两个类中只有很少的变动,⽽最关健的FOR却没有⼀点变动,可是当我分别运⾏这两个程序的时候却出现⼀个很严重的问题,⼀个程序循环的快,⼀个循环的慢.这到底是怎么回事呢~苦苦寻找了半天也没有想到是为什么,因为程序改变的部分根不影响我循环的速度,可是结果却是有很⼤的差别,⼀个⼤约是在⼀分钟这内就可以循环完,可是另⼀个却需要六七分钟,这根本就不是⼀个数据理级的⿇.两个完全⼀样的循环,从代码上根本上是看不出有什么问题.不得以求助同事吧,可是同事看了也感觉很诡异,两个⼈在那订着代码⼜看了⼀个多⼩时,最后同事让我来个⼲净点的,关机重启.我到也听话,就顺着同事的意思去了,可就在关机的这个时候他突然说是不是内存的问题,我也空然想到了,还真的有可能是内存的问题,因为快的那个在我之前运⾏程序之前可给过1G的内存啊,⽽后来的这个我好像是没有设过内存啊,机器起来了,有了这个想法进去看看吧,结果正中要害,果真是慢的那个没有开内存,程序运⾏时只不过是JVM默认开的内存.我初步分析是因为内存太⼩,⽽我的程序所⽤内存⼜正好卡在JVM所开内存边上,不⾄于溢出.当程序运⾏时就得花费⼤部分时间去调⽤GC去,这样就导致了为什么相同的循环出现两种不同的效率~!内存使⽤情况的⽅法:public static String getMemUsage() {long free = ng.Runtime.getRuntime().freeMemory();long total = ng.Runtime.getRuntime().totalMemory();StringBuffer buf = new StringBuffer();buf.append("[Mem: used ").append((total-free)>>20).append("M free ").append(free>>20).append("M total ").append(total>>20).append("M]");return buf.toString();}google⼀下,⼤概就说JVM是这样来操作内存:堆(Heap)和⾮堆(Non-heap)内存 按照官⽅的说法:"Java 虚拟机具有⼀个堆,堆是运⾏时数据区域,所有类实例和数组的内存均从此处分配.堆是在 Java 虚拟机启动时创建的.""在JVM中堆之外的内存称为⾮堆内存(Non-heap memory)".可以看出JVM主要管理两种类型的内存:堆和⾮堆.简单来说堆就是Java代码可及的内存,是留给开发⼈员使⽤的;⾮堆就是JVM留给⾃⼰⽤的,所以⽅法区,JVM内部处理或优化所需的内存(如JIT编译后的代码缓存),每个类结构(如运⾏时常数池,字段和⽅法数据)以及⽅法和构造⽅法的代码都在⾮堆内存中.堆内存分配 JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最⼤分配的内存由-Xmx指定,默认是物理内存的1/4.默认空余堆内存⼩于40%时,JVM就会增⼤堆直到-Xmx的最⼤限制;空余堆内存⼤于70%时, JVM会减少堆直到-Xms的最⼩限制.因此服务器⼀般设置-Xms,-Xmx相等以避免在每次GC 后调整堆的⼤⼩.⾮堆内存分配 JVM使⽤-XX:PermSize设置⾮堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最⼤⾮堆内存的⼤⼩,默认是物理内存的1/4. JVM内存限制(最⼤值) ⾸先JVM内存⾸先受限于实际的最⼤物理内存,假设物理内存⽆限⼤的话,JVM内存的最⼤值跟操作系统有很⼤的关系.简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给⼀个限制,这个限制⼀般是 2GB-3GB(⼀般来说Windows系统下为1.5G-2G,Linux 系统下为2G-3G),⽽64bit以上的处理器就不会有限制了JVM内存的调优1. Heap设定与垃圾回收Java Heap分为3个区,Young,Old和Permanent.Young保存刚实例化的对象.当该区被填满时,GC会将对象移到Old 区.Permanent区则负责保存反射对象,本⽂不讨论该区.JVM的Heap分配可以使⽤-X参数设定,-Xms:初始Heap⼤⼩-Xmx:java heap最⼤值-Xmn:young generation的heap⼤⼩/年轻代的⼤⼩2.JVM有2个GC线程 第⼀个线程负责回收Heap的Young区. 第⼆个线程在Heap不⾜时,遍历Heap,将Young 区升级为Older区.Older区的⼤⼩等于-Xmx减去-Xmn,不能将-Xms的值设的过⼤,因为第⼆个线程被迫运⾏会降低JVM的性能.3.为什么⼀些程序频繁发⽣GC?有如下原因: l 程序内调⽤了System.gc()或Runtime.gc(). l ⼀些中间件软件调⽤⾃⼰的GC⽅法,此时需要设置参数禁⽌这些GC. l Java的Heap太⼩,⼀般默认的Heap值都很⼩. l 频繁实例化对象,Release对象.此时尽量保存并重⽤对象,例如使⽤StringBuffer()和String(). 如果你发现每次GC后,Heap的剩余空间会是总空间的50%,这表⽰你的Heap处于健康状态.许多Server端的Java程序每次GC后最好能有65%的剩余空间.经验之谈:注意: 1.Server端JVM最好将-Xms和-Xmx设为相同值.为了优化GC,最好让-Xmn值约等于-Xmx的1/3[2]. 2.⼀个GUI程序最好是每10到20秒间运⾏⼀次GC,每次在半秒之内完成[2].注意: 1.增加Heap的⼤⼩虽然会降低GC的频率,但也增加了每次GC的时间.并且GC运⾏时,所有的⽤户线程将暂停,也就是GC期间,Java应⽤程序不做任何⼯作. 2.Heap⼤⼩并不决定进程的内存使⽤量.进程的内存使⽤量要⼤于-Xmx定义的值,因为Java为其他任务分配内存,例如每个线程的Stack 等. 3.Stack的设定 每个线程都有他⾃⼰的Stack.-Xss 每个线程的Stack⼤⼩,Stack的⼤⼩限制着线程的数量.如果Stack过⼤就好导致内存溢漏.-Xss参数决定Stack⼤⼩,例如-Xss1024K.如果Stack太⼩,也会导致Stack溢漏.4.硬件环境 硬件环境也影响GC的效率,例如机器的种类,内存,swap空间,和CPU的数量. 如果你的程序需要频繁创建很多transient对象,会导致JVM频繁GC.这种情况你可以增加机器的内存,来减少Swap空间的使⽤[2].5.4种GC 第⼀种为单线程GC,也是默认的GC.,该GC适⽤于单CPU机器. 第⼆种为Throughput GC,是多线程的GC,适⽤于多CPU,使⽤⼤量线程的程序.第⼆种GC与第⼀种GC相似,不同在于GC在收集Young区是多线程的,但在Old区和第⼀种⼀样,仍然采⽤单线程.-XX:+UseParallelGC参数启动该GC. 第三种为Concurrent Low Pause GC,类似于第⼀种,适⽤于多CPU,并要求缩短因GC造成程序停滞的时间.这种GC可以在Old区的回收同时,运⾏应⽤程序.-XX:+UseConcMarkSweepGC参数启动该GC. 第四种为Incremental Low Pause GC,适⽤于要求缩短因GC造成程序停滞的时间.这种GC可以在Young区回收的同时,回收⼀部分Old区对象.-Xincgc参数启动该GC.6. GC如何运作? 分代收集,在新⽣代区频繁发⽣,在⽼年区较少发⽣,永久区基本不发⽣7.GC的四⼤算法 1、复制算法 效率⾼,不会产⽣内存碎⽚,但需要双倍空间,在新⽣代区中使⽤ 2、标记清除算法 ⽆需双倍空间,但会产⽣内存碎⽚,但需要进⾏两次扫描,在⽼年代区中使⽤ 3.标记压缩算法 ⽆需双倍空间,不会产⽣内存碎⽚,但移动对象需要时间,在⽼年代中使⽤ 4.标记清除压缩算法。
jvm标准参数

jvm标准参数JVM标准参数JVM(Java Virtual Machine)是一种虚拟机,它是Java程序的执行环境。
在JVM中,有很多参数可以设置,这些参数可以通过命令行或者配置文件来设置。
这篇文章将详细介绍JVM标准参数。
一、概述JVM标准参数分为两类:通用参数和非通用参数。
通用参数适用于所有的JVM实现,而非通用参数只适用于某些特定的实现。
二、通用参数1. -Xms-Xms是指JVM启动时堆内存的最小值。
例如,-Xms512m表示JVM启动时堆内存的最小值为512MB。
2. -Xmx-Xmx是指JVM启动时堆内存的最大值。
例如,-Xmx1024m表示JVM启动时堆内存的最大值为1GB。
3. -Xmn-Xmn是指新生代的大小。
新生代是指在垃圾收集中被划分为年轻代和幸存区的内存区域。
例如,-Xmn256m表示新生代大小为256MB。
4. -XX:PermSize-XX:PermSize是指永久代(Permanent Generation)大小。
永久代是一种特殊的内存区域,它主要用于存放类信息、常量池等数据。
例如,-XX:PermSize=64m表示永久代大小为64MB。
5. -XX:MaxPermSize-XX:MaxPermSize是指永久代的最大值。
例如,-XX:MaxPermSize=128m表示永久代的最大值为128MB。
6. -Xss-Xss是指每个线程的堆栈大小。
例如,-Xss256k表示每个线程的堆栈大小为256KB。
7. -XX:+UseParallelGC-XX:+UseParallelGC是指使用并行垃圾收集器。
并行垃圾收集器可以在多个CPU上同时进行垃圾收集,从而提高垃圾收集效率。
8. -XX:+UseConcMarkSweepGC-XX:+UseConcMarkSweepGC是指使用CMS(Concurrent Mark Sweep)垃圾收集器。
CMS垃圾收集器可以在应用程序运行时进行垃圾回收,从而减少应用程序停顿时间。
JVM常用监控指标解释

JVM常用监控指标解释JVM(Java虚拟机)是Java程序的运行环境,负责将Java程序转化为能够在计算机上运行的机器码。
JVM的监控指标是用来评估和优化Java应用程序的性能的。
下面是一些常用的JVM监控指标的解释:1. 堆内存使用情况:堆是用来存储Java对象的内存区域,监控堆内存使用情况可以帮助我们了解应用程序的内存需求。
堆的使用情况可以划分为两个方面:堆内存的总大小和已使用堆内存的大小。
如果堆内存总大小过小,可能导致OutOfMemory错误;如果已使用堆内存过大,可能导致垃圾回收的频繁发生。
2.垃圾回收情况:垃圾回收是JVM自动管理内存的过程,当对象不再被引用时,JVM会回收这部分内存以供其他对象使用。
垃圾回收的指标包括垃圾回收的次数、垃圾回收的时间、垃圾回收器的类型等。
监控垃圾回收可以帮助我们确定是否存在内存泄漏或垃圾回收频繁的问题。
3.线程数:线程是一种执行代码的执行单元,JVM可以支持并发执行多个线程。
监控线程数可以帮助我们了解应用程序的并发性能。
线程数过高可能会导致系统资源的竞争,线程数过低可能会导致并发执行的效果不佳。
4. 类加载情况:JVM在运行过程中会动态加载Java类。
监控类加载情况可以帮助我们了解应用程序的类加载需求和性能。
类加载的指标包括已加载类的数量、加载类的时间等。
类加载数量过多或加载时间过长可能会导致应用程序启动缓慢或响应时间延迟。
5.CPU使用情况:监控CPU使用情况可以帮助我们了解应用程序的计算资源需求和负载情况。
CPU使用情况包括CPU的使用率、CPU的消耗时间等指标。
高的CPU使用率可能是由于应用程序的计算密集型任务导致的,而低的CPU使用率可能是由于I/O等待导致的。
6.IO等待情况:IO等待是指应用程序在执行过程中等待I/O操作完成的时间。
监控IO等待情况可以帮助我们了解应用程序的IO负载情况。
IO等待的指标包括IO等待的次数、等待的时间等。
高的IO等待次数和时间可能会导致应用程序的响应时间延迟。
JVM运行机制及其原理

JVM运行机制及其原理JVM(Java Virtual Machine)是Java虚拟机的缩写,是运行Java 字节码的虚拟计算机。
它是Java平台的核心组件,负责在不同的操作系统上执行Java程序。
JVM运行机制主要包括类加载、字节码解释、即时编译、垃圾收集等过程,下面将详细介绍JVM运行机制及其原理。
1.类加载当一个Java程序被运行时,JVM会首先加载程序的主类,然后根据程序的依赖关系逐步加载相关的类。
类加载过程主要分为加载、验证、准备、解析和初始化几个阶段:-加载:通过类加载器将类文件加载到内存中。
- 验证:确保加载的类符合Java语言规范和JVM规范。
-准备:为类的静态变量分配内存并初始化为默认值。
-解析:将符号引用转换为直接引用。
-初始化:执行类的初始化方法。
2.字节码解释加载完类文件后,JVM会通过解释器将字节码文件逐条解释执行,将每条字节码翻译成对应的机器代码并执行。
这种方式简单直接,但效率较低,适用于少量代码和频繁切换的情况。
3.即时编译4.垃圾收集JVM还负责管理程序的内存,包括分配内存、回收无用内存等。
在Java中,内存是通过堆和栈来管理的,堆用于存放对象实例,栈用于存放基本数据类型和方法调用。
JVM通过垃圾收集器来管理堆内存,自动回收不再使用的对象,并将内存释放出来供其他对象使用。
5.类加载器类加载器是JVM的重要组成部分,负责加载class文件,并将其转换成JVM可以识别的数据结构。
JVM中存在多个类加载器,分为三个级别:启动类加载器、扩展类加载器和应用程序类加载器。
类加载器采用双亲委派模型,当需要加载一个类时,先委托给父类加载器加载,只有当父类加载器无法找到类时,才由自己加载。
6.内存模型JVM中的内存分为程序计数器、虚拟机栈、本地方法栈、堆、方法区等几部分。
程序计数器记录当前指令执行的位置;虚拟机栈用于存放局部变量表和操作数栈;本地方法栈用于支持本地方法调用;堆用于存放对象实例;方法区用于存放类信息、静态变量等。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
JVM详解本文详细讲解了JVM(Java Virtual Machine)的方方面面,首先由java的特性来描绘JVM 的大致应用,再细细阐述了JVM的原理及内存管理机制和调优.最后讲述了与JVM密切相关的Java GC机制.本文内容大多来自网络,但内容十分丰富,是学习JVM的好资料.后面会再针对JVM的两大职责class loader和execution engine进行讲解若有疑问<ymkyve501@>目录Java相关 (2)1.1Java定义 (2)1.2Java的开发流程 (2)1.3Java运行的原理 (3)1.4半编译半解释 (4)1.5平台无关性 (5)JVM内存模型 (5)2.1JVM规范 (6)2.2 Sun JVM (9)2.3 SUN JVM内存管理(优化) (10)2.4 SUN JVM调优 (13)2.5.JVM简单理解 (16)2.5.1Java栈 (16)2.5.2堆 (16)2.5.3堆栈分离的好处 (19)2.5.4 堆(heap)和栈(stack) (19)JAVA垃圾收集器 (20)3.1垃圾收集简史 (20)3.2常见的垃圾收集策略 (20)3.2.1Reference Counting(引用计数) (20)3.2.2跟踪收集器 (21)3.3JVM的垃圾收集策略 (25)3.3.1Serial Collector (25)3.3.2 Parallel Collector (25)3.3.3 Concurrent Collector (26)Java虚拟机(JVM)参数配置说明 (26)Java相关1.1Java定义1.2Java的开发流程1.3Java运行的原理1.4半编译半解释1.5平台无关性JVM内存模型2.1JVM规范JVM specification对JVM内存的描述首先我们来了解JVM specification中的JVM整体架构。
如下图:主要包括两个子系统和两个组件: Class loader(类装载器) 子系统,Execution engine(执行引擎) 子系统;Runtime data area (运行时数据区域)组件, Native interface(本地接口)组件。
Class loader子系统的作用:根据给定的全限定名类名(如ng.Object)来装载class文件的内容到 Runtime data area中的method area(方法区域)。
Javsa程序员可以extends ng.ClassLoader类来写自己的Class loader。
Execution engine子系统的作用:执行classes中的指令。
任何JVM specification实现(JDK)的核心是Execution engine,换句话说:Sun 的JDK 和IBM的JDK好坏主要取决于他们各自实现的Execution engine的好坏。
每个运行中的线程都有一个Execution engine的实例。
Native interface组件:与native libraries交互,是其它编程语言交互的接口。
Runtime data area 组件:这个组件就是JVM中的内存。
下面对这个部分进行详细介绍。
Runtime data area的整体架构图Runtime data area 主要包括五个部分:Heap (堆), Method Area(方法区域), Java Stack(java的栈), Program Counter(程序计数器), Native method stack(本地方法栈)。
Heap 和Method Area是被所有线程的共享使用的;而Java stack, Program counter 和Native method stack是以线程为粒度的,每个线程独自拥有。
HeapJava程序在运行时创建的所有类实或数组都放在同一个堆中。
而一个Java虚拟实例中只存在一个堆空间,因此所有线程都将共享这个堆。
每一个java程序独占一个JVM实例,因而每个java程序都有它自己的堆空间,它们不会彼此干扰。
但是同一java程序的多个线程都共享着同一个堆空间,就得考虑多线程访问对象(堆数据)的同步问题。
(这里可能出现的异常ng.OutOfMemoryError: Java heap space)Method area在Java虚拟机中,被装载的class的信息存储在Method area的内存中。
当虚拟机装载某个类型时,它使用类装载器定位相应的class文件,然后读入这个class文件内容并把它传输到虚拟机中。
紧接着虚拟机提取其中的类型信息,并将这些信息存储到方法区。
该类型中的类(静态)变量同样也存储在方法区中。
与Heap 一样,method area是多线程共享的,因此要考虑多线程访问的同步问题。
比如,假设同时两个线程都企图访问一个名为Lava的类,而这个类还没有内装载入虚拟机,那么,这时应该只有一个线程去装载它,而另一个线程则只能等待。
(这里可能出现的异常ng.OutOfMemoryError: PermGen full)Java stackJava stack以帧为单位保存线程的运行状态。
虚拟机只会直接对Java stack执行两种操作:以帧为单位的压栈或出栈。
每当线程调用一个方法的时候,就对当前状态作为一个帧保存到java stack中(压栈);当一个方法调用返回时,从java stack弹出一个帧(出栈)。
栈的大小是有一定的限制,这个可能出现StackOverFlow问题。
下面的程序可以说明这个问题。
public class TestStackOverFlow {public static void main(String[] args) {Recursive r = new Recursive();r.doit(10000);// Exception in thread "main"ng.StackOverflowError}}class Recursive {public int doit(int t) {if (t <= 1) {return 1;}return t + doit(t - 1);}}Program counter每个运行中的Java程序,每一个线程都有它自己的PC寄存器,也是该线程启动时创建的。
PC寄存器的内容总是指向下一条将被执行指令的饿“地址”,这里的“地址”可以是一个本地指针,也可以是在方法区中相对应于该方法起始指令的偏移量。
Native method stack对于一个运行中的Java程序而言,它还能会用到一些跟本地方法相关的数据区。
当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。
本地方法可以通过本地方法接口来访问虚拟机的运行时数据区,不止如此,它还可以做任何它想做的事情。
比如,可以调用寄存器,或在操作系统中分配内存等。
总之,本地方法具有和JVM相同的能力和权限。
(这里出现JVM无法控制的内存溢出问题native heap OutOfMemory )2.2 Sun JVMSun JVM中对JVM Specification的实现(内存部分)JVM Specification只是抽象的说明了JVM实例按照子系统、内存区、数据类型以及指令这几个术语来描述的,但是规范并非是要强制规定Java虚拟机实现内部的体系结构,更多的是为了严格地定义这些实现的外部特征。
Sun JVM实现中:Runtime data area(JVM 内存) 五个部分中的Java Stack , Program Counter, Native method stack三部分和规范中的描述基本一致;但对Heap 和Method Area进行了自己独特的实现。
这个实现和Sun JVM 的Garbage collector(垃圾回收)机制有关,下面的章节进行详细描述。
垃圾分代回收算法(Generational Collecting)基于对对象生命周期分析后得出的垃圾回收算法。
把对象分为年青代、年老代、持久代,对不同生命周期的对象使用不同的算法(上述方式中的一个)进行回收。
现在的垃圾回收器(从J2SE1.2开始)都是使用此算法的。
如上图所示,为Java堆中的各代分布。
1. Young(年轻代)JVM specification中的Heap的一部份年轻代分三个区。
一个Eden区,两个Survivor区。
大部分对象在Eden区中生成。
当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor 区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。
需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。
而且,Survivor区总有一个是空的。
2. Tenured(年老代)JVM specification中的Heap的一部份年老代存放从年轻代存活的对象。
一般来说年老代存放的都是生命期较长的对象。
3. Perm(持久代)JVM specification中的Method area用于存放静态文件,如今Java类、方法等。
持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。
持久代大小通过-XX:MaxPermSize=进行设置。
2.3 SUN JVM内存管理(优化)在我做J2EE系统开发的工作生涯中,经常遇到技术人员或客户发出诸如此类的感慨:我的J2EE应用系统处理的数据量不大,系统体积也不大,技术架构也没有问题,我的应用服务器的内存有4G或8G;系统运行起来很慢,还经常出现内存溢出错误。
真是无奈!每次遇到这样的情况,我心中都会忍不住窃笑之。
其实他们所遇到这种情况,不是技术架构上的问题,不是系统本身的问题,也不是应用服务器的问题,也可能不是服务器的内存资源真的不足的问题。
他们花了很多时间在J2EE应用系统本身上找问题(当然一般情况下,这种做法是对的;当出现问题时,在自身上多找找有什么不足),结果还是解决不了问题。