JVM和垃圾回收

来源:互联网 发布:windows找不到regedit 编辑:程序博客网 时间:2024/04/29 13:31

一、JVM

虚拟机大体上分为2种:1、系统虚拟机  2、程序虚拟机


1、类加载子系统:负责从文件系统或者网络中加载class文件,加载的信息存放在方法区里面。

2、方法区:存放类信息、常量信息、常量池信息,包括字符串常量,数字常量等等。

3、Java堆:java虚拟机启动的时候会建立java堆,他是java程序主要的内存工作区域。几乎所有对象的实例都放在堆中。堆所有线程共享的。

4、直接内存:Java的IO库允许java程序使用直接内存,从而提高性能。通常直接内存速度会高于java堆。读写频繁的时候可以使用。

5:每个虚拟机线程都有一个私有的栈,一个线程的栈在线程创建的时候被创建,栈中存放局部变量,方法参数。

6:本地方法栈和java栈非常类似,最大的不同是本地方法栈用于本地方法调用

7、垃圾回收系统是java的核心.java有一套垃圾清理机制,不需要我们手动清理。

8、PC寄存器:PC寄存器是每个线程的私有空间,java会为每个线程创建PC寄存器。在任意时刻,一个java线程总是在执行一个方法,这个方法成为当前方法,如果该方法不是本地方法,那么PC寄存器就会执行当前正在被执行的命令。如果该方法是本地方法,那么PC寄存器的值为undefined,寄存器存放当前环境执行指针、程序计数器、操作栈指针,计算的变量指针等。

9、执行引擎:负责执行虚拟机的字节码,一般会编译成字节码后执行。


堆、栈、方法区的概念和联系

堆解决的是数据的存储问题,即数据是怎么放,放在那里。

栈解决的是程序的运行问题.即程序如何执行,或者说如何处理数据。

方法区则是辅助堆栈的快永久区。

我们创建一个新的User对象:那么User的一些信息(类信息、静态 信息都存在于方法区中)

                    User类被实例化后存放在java堆中,一块内存空间。

   当我们去使用的时候,都是使用User对象的引用,刑如User user =new  User();

                    这里的user存放在java栈中,即User真实对象的一个引用。


几乎所有对象都放在堆中。并且由堆自动化管理。根据垃圾回收机制的不同,java堆可能会有不同的结构。最为常见的将堆分为新生代和老年代。

新生代分为eden区、s0区、s1区。s0、s1区也被称为from和to区,他们是2块大小相等并且可以互相角色的空间。

大多数情况下,对象被创建的时候存在eden区,经过垃圾回收之后,如果该对象还活着,那么将会进入s0或者s1区,经过每一次新生代回收之后,如果对象还存在那么年龄就+1。到达一定年龄之后,就进入老年区



堆分配参数

-XX+PrintGC:虚拟机启动的时候打印GC日志

-XX+UseSerialGC:配置串行回收器

-XX+printGCDetail:打印GC详细日志

-Xms:设置虚拟机启动时初始化堆大小
-Xmx:设置虚拟机能获得的最大堆大小
总结:在实际工作中-Xms和-Xmx设置相等,减少垃圾回收次数提高性能。

新生代配置:
-Xmn:可以设置新生代的大小,新生代大小一般设置为堆控件的1 /3到1/4左右。
-XX:SurvivorRatio:用来设置新生代中eden空间和form/to空间的比例。含义:-XX:SurvivorRatio=eden/from=eden/to
除了可以设置新生代和老年代的比例:-XX:NewRatio=老年代/新生代

堆溢出处理:
在java程序运行过程中,如果堆空间不足会跑出Out Of Memory  即OOM,我们可以配置:-XX:+HeapDumpOnOutOfMemoryError 
该参数和-XX:HeapDumpPath,可以设置导出堆的存放路径。

方法区:
和java堆一样,方法区是一块所有线程共享的内存区域,它用于保存系统的类信息,方法区(永久区)可以保存信息可以对其进行大量的类,就需要设置一个相对合适的方法区,在默认情况下 -XX:MaxPermSize为64MB,如果系统运行的时产生大量的类,就需要设置一个合适的方法区,以免出现永久区内存溢出问题
XX:PermSize=64MB  -XX:MaxPermSize=64MB



二、垃圾回收
GC中的垃圾,特指存在于内存中、不会在再被使用的对象,垃圾回收有很多算法,如:引用计算器、标记压缩发、复制算法、分代、分区的思想
  
1、垃圾回收算法:
引用计数器:对于对象设置一个引用计数器,每增加一个变量对它的引用,引用计数器就会加1,每减少一个变量的引用,引用计数器就会减1,只有当对象的引用计数器变成0时,该对象才会被回收。该算法存在的问题:
是这种方法无法处理循环引用的情况。再解释下什么是循环引用,假设有两个对象 A和B,A中引用了B对象,并且B中也引用了A对象, 那么这时两个对象的引用计数器都不为0,但是由于存在相互引用导致无法垃圾回收A和 B,导致内存泄漏。

标记压缩法(Java中老年代采用):将垃圾回收分成了两个阶段:标记阶段和清除阶段。在标记阶段,通过跟对象,标记所有从跟节点开始的可达的对象,那么未标记的对象就是未被引用的垃圾对象。在清除阶段,清除掉所以的未被标记的对象。这种情况会产生磁盘碎片。所以这标记完成之后,清除之前,需要将这些标记过的对象集中放到一起,这样再去清除。

复制算法(Java中新生代采用): 核心思想是将内存空间分成两块,同一时刻只使用其中的一块,在垃圾回收时将正在使用的内存中的存活的对象复制到未使用的内存中,然后清除正在使用的内存块中所有的对象。

分代算法:根据对象特点把内存分为N块,根据每个内存的特点使用不同的算法。
对于老年代和新生代来说,老年代回收频率低,每次耗时长,新生代回收频率高,每次耗时短。我们应该减少老年代的回收次数。

分区算法:将整个内存区域分为一个一个的小区,每个区域都是可以独立运行的空间。减少回收的粒度,从而提高性能。

2、垃圾回收时的停顿现象
垃圾回收的任务是识别垃圾并清理。大部分情况下,会要求系统进入一个停顿的状态,停顿的目的是终止是由的线程,只有这样系统才不会产生新的垃圾。
3、对象如何进入老年代
对象被创建的时候存在eden区,经过垃圾回收之后,如果该对象还活着,那么将会进入s0或者s1区,经过每一次新生代回收之后,如果对象还存在那么年龄就+1。到达一定年龄之后。虚拟机提供了一个参数来控制新生代对象的最大年龄,超过这个年龄就会进入老年代
-XX:ManTenuringThreshold,默认状态是15
另外如果对象过大,那么数据则会直接进入老年区,参数配置为:
-XX:PretenureSizeThreshhold
4、TLAB
TLAB全称是Thread Local Allcation Buffer 即线程本地分配缓存。从名字上看是一个线程专用的内存分配区域,是为了加速对象分配而生,每一个线程都有创建TLAB。该线程独享工作区域。java虚拟机使用这种区域来避免线程冲突问题。这种区域一般不会太大。如果对象太大会直接分配到堆空间中。
-XX:+UseTLAB 使用TLAB区域  -XX:-UseTLAB   不使用TLAB区域
-XX:+TLABSIZE 设置TLAB大小
-XX:TLABRefillWasteFraction设置维护进入TLAB的单个对象大小,他说一个比例值,默认是64,即如果对象大于整个空间的1/64,则在堆创建空间
-XX:PrintTLAB查看TLAB信息
-XX:Resize 自行调整TLABRefillWasteFraction阈值。
5垃圾回收器
   垃圾回收器有:串行垃圾回收器、并行垃圾回收器、CMS回收器、G1回收器
   1)、串行垃圾回收器:是指使用单线程进行垃圾回收,每次回收的时候只有个工作线程,对于并行能力较弱的服务器来说,串行回收器的专注性和独立性往往有更好的性能表现,串行回收器可以在新生代和老年代中使用,根据作用于不同的堆空间,分为新生代串行回收器和老年代串行回收器
使用-XX:+UseSerialGC 参数可以设置使用新生代串行回收器和老年代串行回收器。
   2)、并行回收器是在串行回收器的基础上进行了改进,可以使用多线程进行垃圾回收,对于性能好的计算机而言,可以有效缩短垃圾回收所需时间。
   ParNew回收器是一个工作在新生代和老年代的垃圾回收器。他只是一个简单的串行回收器线程化,他的回收策略和酸和串行回收器是是一样的。
使用-XX:+UseParNewGC 新生代ParNew回收器,老年代则使用串行回收器  ParNew回收器工作时的线程数量可以使用 -XX ParallelGCTheads参数指定。一般最好和计算机的CPU相当。避免线程过多影响性能。
   3):并行回收器(ParallelGC回收器):新生代ParallelGC回收器,使用了复制算法的收集器,也是多线程独占形式的收集器,但ParallelGC回收器有个非常重要的特点,他是非常关注系统的吞吐量。-XX:MaxGCPauseMills:设置最大的垃圾收集停顿时间。可以把虚拟机垃圾收集的停顿时间控制在MaxGCPauseMills范围,如果希望减少GC停顿时间可以将MaxGCPauseMills设置的很小,但是会导致GC频繁。从而增加了GC总时间,所以根据需要设置该值。
-XX:GCTimeRatio:设置吞吐量大小,他说一个0-100的整数,默认情况下他的取值为99,那么系统将花费不超过1/(1+n)的时间用于垃圾回收,也就是1/(1+99),
  另外也可以指定自适应模式:-XX:+UseAdaptiveSizePolicy.在该模式下,新生代、eden、form/to区以及老年代晋升年龄参数会被自动调整。已达到堆大小,吞吐量和停顿时间的平衡点。
并行回收器(parallelOldGC):该回收器也是一种多线程垃圾回收器,和新生代的ParallelGC也是一种关注吞吐量的回收器。-XX:+UseparallelOldGC进行设置使用该回收器
-XX:+ParallelGCThreads也可以设置垃圾回收的线程数量
4)CMS回收器:CMS全称为Concurrent Mark Sweep,意为并发标记删除,他是用标记清除法,主要关注系统停顿时间
 -XX:+UseConcMarkSweepGC进行设置
 -XX:+ConcGCTheads设置并发线程数量
CMS回收器并不是独占的回收器,也就是说在CMS回收的过程中,应用程序仍然在进行工作,所以会有新的垃圾产生,所以在垃圾回收的过程中要保证有足够多的内存。CMS不会在垃圾饱和的时候才进行垃圾回收,而是在某一阀值的时候就进行回收:-XX:CMSInitiatingOccupancyFraction来指定,默认是68,也就是说在老年代使用率达到68%的时候就会进行垃圾回收。如果在CMS垃圾回收出现内存不足的时候,虚拟机将启动老年代的串行回收器。CMS回收器使用的算法是标记清除法,所以会出现内存碎片,那么CMS有个参数可以设置-XX:UseCMSCompactAtFullCollction可以CMS完成后进行一次碎片整理,-XX:CMSFullGCBeforeCompaction参数可以设置多少次垃圾回收后,对内存进行一次压缩。
5)G1回收器:
G1回收器是在JDK1.7之后提出的。属于分代垃圾回收器,区分老年代和新生代,依然有edan、from/to区
并行性:G1回收期间可以多个线程同事工作。
并发性:G1拥有与应用程序交替执行的能力,部分工作可以与应用程序同时执行。不会完全阻塞应用程序。
分代GC:以前的回收器要么在老年代种使用,那么在新生代中使用。而G1回收器可以在老年代和新生代中工作。
空间整理:G1回收过程中,不会像CMS那样若干次GC后进行碎片处理。G1采用有效复制对象的形式,减少空间碎片
可预见性:G1回收器只选取部分区域进行回收,减少了回收范围,提高了性能。
使用-XX:+UseG1GC 使用G1回收器
使用-XX:MaxGCPauseMills指定最大停顿时间
使用-XX:ParallelGCThreads 设置并行回收的线程数量


   











0 0