网站建设内存,手机 网站 尺寸,网站系统与程序的链接,在校学生兼职网站建设1#xff1a;什么是JVM JVM是Java Virtual Machine#xff08;Java虚拟机#xff09;的缩写#xff0c;JVM是一种用于计算设备的规范#xff0c;它是一个虚构出来的计算机#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令… 1什么是JVM JVM是Java Virtual MachineJava虚拟机的缩写JVM是一种用于计算设备的规范它是一个虚构出来的计算机是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息使Java程序只需生成在Java虚拟机上运行的目标代码字节码,就可以在多种平台上不加修改地运行。JVM在执行字节码时实际上最终还是把字节码解释成具体平台上的机器指令执行。 2JRE/JDK/JVM是什么关系 JRE(JavaRuntimeEnvironmentJava运行环境)也就是Java平台。所有的Java 程序都要在JRE下才能运行。普通用户只需要运行已开发好的java程序安装JRE即可。 JDK(Java Development Kit)是程序开发者用来来编译、调试java程序用的开发工具包。JDK的工具也是Java程序也需要JRE才能运行。为了保持JDK的独立性和完整性在JDK的安装过程中JRE也是 安装的一部分。所以在JDK的安装目录下有一个名为jre的目录用于存放JRE文件。 JVM(JavaVirtualMachineJava虚拟机)是JRE的一部分。它是一个虚构出来的计算机是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构如处理器、堆栈、寄存器等还具有相应的指令系统。Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关实现跨平台。 3JVM原理 JVM是java的核心和基础在java编译器和os平台之间的虚拟处理器。它是一种利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台可以在上面执行java的字节码程序。 java编译器只要面向JVM生成JVM能理解的代码或字节码文件。Java源文件经编译成字节码程序通过JVM将每一条指令翻译成不同平台机器码通过特定平台运行。 4JVM的体系结构 类装载器ClassLoader用来装载.class文件 执行引擎执行字节码或者执行本地方法 运行时数据区方法区、堆、java栈、PC寄存器、本地方法栈 5JVM运行时数据区 第一块PC寄存器 PC寄存器是用于存储每个线程下一步将执行的JVM指令如该方法为native的则PC寄存器中不存储任何信息。 第二块JVM栈 JVM栈是线程私有的每个线程创建的同时都会创建JVM栈JVM栈中存放的为当前线程中局部基本类型的变量java中定义的八种基本类型boolean、char、byte、short、int、long、float、double、部分的返回结果以及Stack Frame非基本类型的对象在JVM栈上仅存放一个指向堆上的地址。 第三块堆Heap 它是JVM用来存储对象实例以及数组值的区域可以认为Java中所有通过new创建的对象的内存都在此分配Heap中的对象的内存需要等待GC进行回收。 1 堆是JVM中所有线程共享的因此在其上进行对象内存的分配均需要进行加锁这也导致了new对象的开销是比较大的 2 Sun Hotspot JVM为了提升对象内存分配的效率对于所创建的线程都会分配一块独立的空间TLABThread Local Allocation Buffer其大小由JVM根据运行的情况计算而得在TLAB上分配对象时不需要加锁因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配在这种情况下JVM中分配对象内存的性能和C基本是一样高效的但如果对象过大的话则仍然是直接使用堆空间分配 3 TLAB仅作用于新生代的Eden Space因此在编写Java程序时通常多个小的对象比大的对象分配起来更加高效。 4 所有新创建的Object 都将会存储在新生代Yong Generation中。如果Young Generation的数据在一次或多次GC后存活下来那么将被转移到OldGeneration。新的Object总是创建在Eden Space。 第四块方法区域Method Area 1在Sun JDK中这块区域对应的为PermanetGeneration又称为持久代。 2方法区域存放了所加载的类的信息名称、修饰符等、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时这些数据都来源于方法区域同时方法区域也是全局共享的在一定的条件下它也会被GC当方法区域需要使用的内存超过其允许的大小时会抛出OutOfMemory的错误信息。 第五块运行时常量池Runtime Constant Pool 存放的为类中的固定的常量信息、方法和Field的引用信息等其空间从方法区域中分配。 第六块本地方法堆栈Native Method Stacks JVM采用本地方法堆栈来支持native方法的执行此区域用于存储每个native方法调用的状态。 6对象“已死”的判定算法 由于程序计数器、Java虚拟机栈、本地方法栈都是线程独享其占用的内存也是随线程生而生、随线程结束而回收。而Java堆和方法区则不同线程共享是GC的所关注的部分。 在堆中几乎存在着所有对象GC之前需要考虑哪些对象还活着不能回收哪些对象已经死去可以回收。 有两种算法可以判定对象是否存活 1.引用计数算法给对象中添加一个引用计数器每当一个地方应用了对象计数器加1当引用失效计数器减1当计数器为0表示该对象已死、可回收。但是它很难解决两个对象之间相互循环引用的情况。 2.可达性分析算法通过一系列称为“GC Roots”的对象作为起点从这些节点开始向下搜索搜索所走过的路径称为引用链当一个对象到GC Roots没有任何引用链相连即对象到GC Roots不可达则证明此对象已死、可回收。Java中可以作为GC Roots的对象包括虚拟机栈中引用的对象、本地方法栈中Native方法引用的对象、方法区静态属性引用的对象、方法区常量引用的对象。 在主流的商用程序语言如我们的Java的主流实现中都是通过可达性分析算法来判定对象是否存活的。 7JVM垃圾回收 GC (Garbage Collection)的基本原理将内存中不再被使用的对象进行回收GC中用于回收的方法称为收集器由于GC需要消耗一些资源和时间Java在对对象的生命周期特征进行分析后按照新生代、旧生代的方式来对对象进行收集以尽可能的缩短GC对应用造成的暂停 1对新生代的对象的收集称为minor GC 2对旧生代的对象的收集称为Full GC 3程序中主动调用System.gc()强制执行的GC为Full GC。 不同的对象引用类型 GC会采用不同的方法进行回收JVM对象的引用分为了四种类型 1强引用默认情况下对象采用的均为强引用这个对象的实例没有其他对象引用GC时才会被回收 2软引用软引用是Java中提供的一种比较适合于缓存场景的应用只有在内存不够用的情况下才会被GC 3弱引用在GC时一定会被GC回收 4虚引用由于虚引用只是用来得知对象是否被GC 8垃圾收集算法 1、标记-清除算法 最基础的算法分标记和清除两个阶段首先标记处所需要回收的对象在标记完成后统一回收所有被标记的对象。 它有两点不足一个效率问题标记和清除过程都效率不高一个是空间问题标记清除之后会产生大量不连续的内存碎片类似于我们电脑的磁盘碎片空间碎片太多导致需要分配大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾回收动作。 2、复制算法 为了解决效率问题出现了“复制”算法他将可用内存按容量划分为大小相等的两块每次只需要使用其中一块。当一块内存用完了将还存活的对象复制到另一块上面然后再把刚刚用完的内存空间一次清理掉。这样就解决了内存碎片问题但是代价就是可以用内容就缩小为原来的一半。 3、标记-整理算法 复制算法在对象存活率较高时就会进行频繁的复制操作效率将降低。因此又有了标记-整理算法标记过程同标记-清除算法但是在后续步骤不是直接对对象进行清理而是让所有存活的对象都向一侧移动然后直接清理掉端边界以外的内存。 4、分代收集算法 当前商业虚拟机的GC都是采用分代收集算法这种算法并没有什么新的思想而是根据对象存活周期的不同将堆分为新生代和老年代方法区称为永久代在新的版本中已经将永久代废弃引入了元空间的概念永久代使用的是JVM内存而元空间直接使用物理内存。 这样就可以根据各个年代的特点采用不同的收集算法。 新生代中的对象“朝生夕死”每次GC时都会有大量对象死去少量存活使用复制算法。新生代又分为Eden区和Survivor区Survivor from、Survivor to大小比例默认为8:1:1。 老年代中的对象因为对象存活率高、没有额外空间进行分配担保就使用标记-清除或标记-整理算法。 新产生的对象优先进去Eden区当Eden区满了之后再使用Survivor from当Survivor from 也满了之后就进行Minor GC新生代GC将Eden和Survivor from中存活的对象copy进入Survivor to然后清空Eden和Survivor from这个时候原来的Survivor from成了新的Survivor to原来的Survivor to成了新的Survivor from。复制的时候如果Survivor to 无法容纳全部存活的对象则根据老年代的分配担保类似于银行的贷款担保将对象copy进去老年代如果老年代也无法容纳则进行Full GC老年代GC。 大对象直接进入老年代JVM中有个参数配置-XX:PretenureSizeThreshold令大于这个设置值的对象直接进入老年代目的是为了避免在Eden和Survivor区之间发生大量的内存复制。 长期存活的对象进入老年代JVM给每个对象定义一个对象年龄计数器如果对象在Eden出生并经过第一次Minor GC后仍然存活并且能被Survivor容纳将被移入Survivor并且年龄设定为1。没熬过一次Minor GC年龄就加1当他的年龄到一定程度默认为15岁可以通过XX:MaxTenuringThreshold来设定就会移入老年代。但是JVM并不是永远要求年龄必须达到最大年龄才会晋升老年代如果Survivor 空间中相同年龄如年龄为x所有对象大小的总和大于Survivor的一半年龄大于等于x的所有对象直接进入老年代无需等到最大年龄要求。 9垃圾收集器 垃圾收集算法是方法论垃圾收集器是具体实现。JVM规范对于垃圾收集器的应该如何实现没有任何规定因此不同的厂商、不同版本的虚拟机所提供的垃圾收集器差别较大这里只看HotSpot虚拟机。 JDK7/8后HotSpot虚拟机所有收集器及组合连线如下 1.Serial收集器 Serial收集器是最基本、历史最久的收集器曾是新生代手机的唯一选择。他是单线程的只会使用一个CPU或一条收集线程去完成垃圾收集工作并且它在收集的时候必须暂停其他所有的工作线程直到它结束即“Stop the World”。停掉所有的用户线程对很多应用来说难以接受。比如你在做一件事情被别人强制停掉你心里奔腾而过的“羊驼”还数的过来吗 尽管如此它仍然是虚拟机运行在client模式下的默认新生代收集器简单而高效与其他收集器的单个线程相比因为没有线程切换的开销等。 工作示意图 2.ParNew收集器 ParNew收集器是Serial收集器的多线程版本除了使用了多线程之外其他的行为收集算法、stop the world、对象分配规则、回收策略等同Serial收集器一样。 是许多运行在Server模式下的JVM中首选的新生代收集器其中一个很重还要的原因就是除了Serial之外只有他能和老年代的CMS收集器配合工作。 工作示意图 3.Parallel Scavenge收集器 新生代收集器并行的多线程收集器。它的目标是达到一个可控的吞吐量就是CPU运行用户代码的时间与CPU总消耗时间的比值即 吞吐量行用户代码的时间/[行用户代码的时间垃圾收集时间]这样可以高效率的利用CPU时间尽快完成程序的运算任务适合在后台运算而不需要太多交互的任务。 4.Serial Old收集器 Serial 收集器的老年代版本单线程“标记整理”算法主要是给Client模式下的虚拟机使用。 另外还可以在Server模式下 JDK 1.5之前的版本中雨Parallel Scavenge 收集器搭配使用 可以作为CMS的后背方案在CMS发生Concurrent Mode Failure是使用 工作示意图 5.Parallel Old收集器 Parallel Scavenge的老年代版本多线程“标记整理”算法JDK 1.6才出现。在此之前Parallel Scavenge只能同Serial Old搭配使用由于Serial Old的性能较差导致Parallel Scavenge的优势发挥不出来尴了个尬~~ Parallel Old收集器的出现使“吞吐量优先”收集器终于有了名副其实的组合。在吞吐量和CPU敏感的场合都可以使用Parallel Scavenge/Parallel Old组合。组合的工作示意图如下 6.CMS收集器 CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器停顿时间短用户体验就好。 基于“标记清除”算法并发收集、低停顿运作过程复杂分4步 1)初始标记仅仅标记GC Roots能直接关联到的对象速度快但是需要“Stop The World” 2)并发标记就是进行追踪引用链的过程可以和用户线程并发执行。 3)重新标记修正并发标记阶段因用户线程继续运行而导致标记发生变化的那部分对象的标记记录比初始标记时间长但远比并发标记时间短需要“Stop The World” 4)并发清除清除标记为可以回收对象可以和用户线程并发执行 由于整个过程耗时最长的并发标记和并发清除都可以和用户线程一起工作所以总体上来看CMS收集器的内存回收过程和用户线程是并发执行的。 工作示意图 CSM收集器有3个缺点 1)对CPU资源非常敏感 并发收集虽然不会暂停用户线程但因为占用一部分CPU资源还是会导致应用程序变慢总吞吐量降低。 CMS的默认收集线程数量是(CPU数量3)/4当CPU数量多于4个收集线程占用的CPU资源多于25%对用户程序影响可能较大不足4个时影响更大可能无法接受。 2)无法处理浮动垃圾在并发清除时用户线程新产生的垃圾叫浮动垃圾,可能出现Concurrent Mode Failure失败。 并发清除时需要预留一定的内存空间不能像其他收集器在老年代几乎填满再进行收集如果CMS预留内存空间无法满足程序需要就会出现一次Concurrent Mode Failure失败这时JVM启用后备预案临时启用Serail Old收集器而导致另一次Full GC的产生 3)产生大量内存碎片CMS基于标记-清除算法清除后不进行压缩操作产生大量不连续的内存碎片这样会导致分配大内存对象时无法找到足够的连续内存从而需要提前触发另一次Full GC动作。 7.G1收集器 G1Garbage-First是JDK7-u4才正式推出商用的收集器。G1是面向服务端应用的垃圾收集器。它的使命是未来可以替换掉CMS收集器。 G1收集器特性 并行与并发能充分利用多CPU、多核环境的硬件优势缩短停顿时间能和用户线程并发执行。 分代收集G1可以不需要其他GC收集器的配合就能独立管理整个堆采用不同的方式处理新生对象和已经存活一段时间的对象。 空间整合整体上看采用标记整理算法局部看采用复制算法两个Region之间不会有内存碎片不会因为大对象找不到足够的连续空间而提前触发GC这点优于CMS收集器。 可预测的停顿除了追求低停顿还能建立可以预测的停顿时间模型能让使用者明确指定在一个长度为M毫秒的时间片段内消耗在垃圾收集上的时间不超N毫秒这点优于CMS收集器。 为什么能做到可预测的停顿 是因为可以有计划的避免在整个Java堆中进行全区域的垃圾收集。 G1收集器将内存分大小相等的独立区域Region新生代和老年代概念保留但是已经不再物理隔离。 G1跟踪各个Region获得其收集价值大小在后台维护一个优先列表 每次根据允许的收集时间优先回收价值最大的Region名称Garbage-First的由来 这就保证了在有限的时间内可以获取尽可能高的收集效率。 对象被其他Region的对象引用了怎么办 判断对象存活时是否需要扫描整个Java堆才能保证准确在其他的分代收集器也存在这样的问题而G1更突出新生代回收的时候不得不扫描老年代无论G1还是其他分代收集器JVM都是使用Remembered Set来避免全局扫描每个Region都有一个对应的Remembered Set每次Reference类型数据写操作时都会产生一个Write Barrier 暂时中断操作然后检查将要写入的引用指向的对象是否和该Reference类型数据在不同的 Region其他收集器检查老年代对象是否引用了新生代对象如果不同通过CardTable把相关引用信息记录到引用指向对象的所在Region对应的Remembered Set中进行垃圾收集时在GC根节点的枚举范围加入 Remembered Set 就可以保证不进行全局扫描也不会有遗漏。 不计算维护Remembered Set的操作回收过程可以分为4个步骤与CMS较为相似 1)初始标记仅仅标记GC Roots能直接关联到的对象并修改TAMS(Next Top at Mark Start)的值让下一阶段用户程序并发运行时能在正确可用的Region中创建新对象需要“Stop The World” 2)并发标记从GC Roots开始进行可达性分析找出存活对象耗时长可与用户线程并发执行 3)最终标记修正并发标记阶段因用户线程继续运行而导致标记发生变化的那部分对象的标记记录。并发标记时虚拟机将对象变化记录在线程Remember Set Logs里面最终标记阶段将Remember Set Logs整合到Remember Set中比初始标记时间长但远比并发标记时间短需要“Stop The World” 4)筛选回收首先对各个Region的回收价值和成本进行排序然后根据用户期望的GC停顿时间来定制回收计划最后按计划回收一些价值高的Region中垃圾对象。回收时采用复制算法从一个或多个Region复制存活对象到堆上的另一个空的Region并且在此过程中压缩和释放内存可以并发进行降低停顿时间并增加吞吐量。 工作示意图 10基本结构 从Java平台的逻辑结构上来看我们可以从下图来了解JVM 从上图能清晰看到Java平台包含的各个逻辑模块也能了解到JDK与JRE的区别。