首页 技术 正文
技术 2022年11月7日
0 收藏 879 点赞 1,053 浏览 4338 个字

1 垃圾回收

1.1 JVM的体系结构

1.1.1  JVM

相当与JAVA 的操作系统,是运行JAVA Class文件的程序。

1.1.2  JVM体系

监控调优,运行时内存结构,类加载,calss文件格式,GC

Java字节码和虚拟机执行引擎,线程安全和锁,java内存模型

1.2     JVM的类加载机制

1.2.1  加载过程

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括了:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(Unloading)七个阶段,其中验证、准备、解析三个部分统称链接。

加载(装载)、验证、准备、初始化和卸载这五个阶段顺序是固定的,类的加载过程必须按照这种顺序开始,而解析阶段不一定;它在某些情况下可以在初始化之后再开始,这是为了运行时动态绑定特性(JIT例如接口只在调用的时候才知道具体实现的是哪个子类)。值得注意的是:这些阶段通常都是互相交叉的混合式进行的,通常会在一个阶段执行的过程中调用或激活另外一个阶段。

1.2.2  类加载器

JVM设计者把类加载阶段中的“通过’类全名’来获取定义此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”。

1.类与类加载器

对于任何一个类,都需要由加载它的类加载器和这个类来确立其在JVM中的唯一性。也就是说,两个类来源于同一个Class文件,并且被同一个类加载器加载,这两个类才相等。

2.双亲委派模型

从虚拟机的角度来说,只存在两种不同的类加载器:一种是启动类加载器(Bootstrap ClassLoader),该类加载器使用C++语言实现,属于虚拟机自身的一部分。另外一种就是所有其它的类加载器,这些类加载器是由Java语言实现,独立于JVM外部,并且全部继承自抽象类java.lang.ClassLoader。

从Java开发人员的角度来看,大部分Java程序一般会使用到以下三种系统提供的类加载器:

1)启动类加载器(Bootstrap ClassLoader):负责加载JAVA_HOME\lib目录中并且能被虚拟机识别的类库到JVM内存中,如果名称不符合的类库即使放在lib目录中也不会被加载。该类加载器无法被Java程序直接引用。

2)扩展类加载器(Extension ClassLoader):该加载器主要是负责加载JAVA_HOME\lib\,该加载器可以被开发者直接使用。

3)应用程序类加载器(Application ClassLoader):该类加载器也称为系统类加载器,它负责加载用户类路径(Classpath)上所指定的类库,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。

我们的应用程序都是由这三类加载器互相配合进行加载的,我们也可以加入自己定义的类加载器。这些类加载器之间的关系如下图所示:

1.2.3 
双亲委派

双亲委派模型的工作过程为:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。

1.3    
内存组成及分配

1.3.1 
堆区与非堆区

“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给
自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法
的代码都在非堆内存中。

  • 方法栈&本地方法栈:

    线程创建时产生,方法执行时生成栈帧

  • 方法区

    存储类的元数据信息 常量等

  • java代码中所有的new操作

  • native Memory(C heap)

    Direct Bytebuffer JNI Compile GC;

1.3.2 
堆内存分配

JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指 定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。

1.3.3 
非堆内存分配

JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。

1.4    
垃圾回收

Java中不需要对内存进行手动释放,JVM中的垃圾回收器帮助我们回收内存。

1.4.1 
何时回收

一般来说,当某个区域内存不够的时候就会进行垃圾回收。如:

当Eden区域分配不下对象时,就会进行年轻代的垃圾收集。

1.4.2 
何如判断一块内存是垃圾

即判断一个对象不再被使用,不再使用可以是内有有效的引用。一般来说主要有俩种判断方式。

1)  引用计数法

当有对象引用自身时,就会计数器加1,删除一个引用时就会减1,当计数为0时即可判断为垃圾。引用计数存在循环引用问题,俩个落单的A/B相互引用,但是没有其他对象指向他们。

2)  可达性分析

通过一些根节点开始,分析引用链,没有被引用的对象都可以被标记为垃圾对象。根节点是方法栈中的引用,常量等。

1.5    
垃圾收集算法

1.5.1 
标记清除

对非垃圾对象进行标记,清除其他的对象。这种方式对内存空间造成空隙,即内存碎片。最终导致有空余空间,但没有连续的足够大的空间来分配内存。

1.5.2 
标记整理

标记非垃圾对象后,将这些对象整理好,排列到内存的开始位置,这样内存就是整齐的了。但是因为会造成对象移动,所以效率比较低。

1.5.3 
标记清除整理

上述俩种方式的结合,在若干次清楚后进行一次整理。

1.5.4 
复制

划分俩个大小相同的区域,收集时,将第一个区域的活对象复制到另外一个区域,这样就不会有碎片问题,但是最多只能存放一半的内存,内存使用效率低。

1.6    
垃圾收集器

垃圾收集器是垃圾算法的具有实现。

1.6.1 
Serial New

新生代单线程的收集器,是Client模式默认的垃圾收集器。(JVM分为server和Client俩种模式,server模式在启动时,比Client模式慢10%,但是一旦运行起来之后,性能将会有很大提升;java –version可以查看)

1.6.2 
Parallel New

Serial New的多线程版本。常和CMS搭配使用。

Parallel和concurrent即并行和并发,在垃圾收集这里的表示不同,并行表示有多个线程同时进行垃圾回收,并发是指垃圾收集线程和应用线程可以并发执行。

1.6.3 
Parallel Scanvenge

PS收集器是注重吞吐来那个的收集器

1.6.4 
Serial Old

老年代的单线程收集器

1.6.5 
Parallel Old

Serial Old的多线程版本。

1.6.6 
CMS(concurrent
mark sweep)

注重延迟latency的收集器,在交互式应用中,如果面向用户的WEB应用,需要尽可能较少垃圾收集造成的停顿时间,在总的统计上,吞吐量可能没有PS收集器高。

CMS的四个阶段:

a.  初始标记,标记GC
Root可以直达的对象(STW:在执行垃圾收集算法时,java应用程序的i其他所有除了垃圾回收帮助器线程之外的线程都将会被挂起)

b.  并发标记,从第一部标记的对象开始,进行可达性分析遍历,和应用线程并发执行

c.  重新标记,修正上一阶段并发执行造成的引用变化(STW)

d.  并发清除,并发的清除垃圾。

CMS使用的是标记清除算法,所以有内存碎片的问题,可以设置参数在进行若干次不带整理的收集之后,进行一次带整理的收集。另外,因为垃圾收集是和应用线程并发执行的,在收集的同时可能还会有垃圾产生,即产生了垃圾浮动现象。另外还需要预留出一定空间,到达这个值后进行收集,但是还会有收集速度赶不上产生的速度,这时就会出现concurrent mode failure,CMS会退化成serial
Old 进行GC。

1.6.7 
G1收集器

Java 7中的垃圾收集器。具有大内存收集和目标效率时间控制的能力,目标是代替CMS。G1通过将内存划分成不同的区域(region),并对不同的区域计算分数,分析哪个区域最具有回收价值。

1.7    
Minor GC、major GC和Full GC

1.7.1 
Minor GC

从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC。这一定义既清晰又易于理解。但是,当发生Minor GC事件的时候,有一些有趣的地方需要注意到:

1)当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC。

2)内存池被填满的时候,其中的内容全部会被复制,指针会从0开始跟踪空闲内存。Eden 和 Survivor 区进行了标记和复制操作,取代了经典的标记、扫描、压缩、清理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是停留在所使用内存池的顶部。

3)执行 Minor GC 操作时,不会影响到永久代。从永久代到年轻代的引用被当成 GC roots,从年轻代到永久代的引用在标记阶段被直接忽略掉。

4)质疑常规的认知,所有的 Minor GC 都会触发“全世界的暂停(stop-the-world)”,停止应用程序的线程。对于大部分应用程序,停顿导致的延迟都是可以忽略不计的。其中的真相就 是,大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。如果正好相反,Eden 区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多。

所以
Minor GC 的情况就相当清楚了——每次 Minor GC 会清理年轻代的内存

1.7.2 
Major GC和Full GC

Major GC 是清理老年代。

Full GC 是清理整个堆空间—包括年轻代和老年代。

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,084
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,559
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,408
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,181
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,818
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,901