同步操作将从 Java精选/Ebooks 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
理论上说上32位的JVM堆内存可以到达2^32,即4GB,但实际上会比这个小很多。
不同操作系统之间JVM堆内存不同,如Windows系统大约1.5GB,Solaris大约3GB。64位JVM允许指定最大的堆内存,理论上可以达到2^64,这是一个非常大的数字,实际上可以指定堆内存大小到100GB。甚至有的JVM,如Azul,堆内存到1000G都是可能的。
内存溢出:OutOfMemory,指程序在申请内存时,没有足够的内存空间供其使用。
内存泄露:Memory Leak,指程序在申请内存后,无法释放已申请的内存空间,内存泄漏最终将导致内存溢出。
判断一个对象是否存活有两种方法:
1、引用计数法所谓引用计数法就是给每一个对象设置一个引用计数器,每当有一个地方引用这个对象时,就将计数器加一,引用失效时,计数器就减一。
当一个对象的引用计数器为零时,说明此对象没有被引用,也就是“死对象”,将会被垃圾回收。引用计数法有一个缺陷就是无法解决循环引用问题,也就是说当对象A引用对象B,对象B又引用者对象A,那么此时A,B对象的引用计数器都不为零,也就造成无法完成垃圾回收,所以主流的虚拟机都没有采用这种算法。
2、可达性算法(引用链法)该算法的思想是:从一个被称为GC Roots的对象开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用。在java中可以作为GC Roots的对象有以下几种:
虚拟机栈中引用的对象 方法区类静态属性引用的对象 方法区常量池引用的对象 本地方法栈JNI引用的对象
虽然这些算法可以判定一个对象是否能被回收,但是当满足上述条件时,一个对象比不一定会被回收。
当一个对象不可达GC Root时,这个对象并不会立马被回收,而是处于一个死缓的阶段,若要被真正的回收需要经历两次标记如果对象在可达性分析中没有与GC Root的引用链,那么此时就会被第一次标记并且进行一次筛选,筛选的条件是是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者已被虚拟机调用过,那么就认为是没必要的。
如果该对象有必要执行finalize()方法,那么这个对象将会放在一个称为F-Queue的对队列中,虚拟机会触发一个Finalize()线程去执行,此线程是低优先级的,并且虚拟机不会承诺一直等待它运行完,这是因为如果finalize()执行缓慢或者发生了死锁,那么就会造成F-Queue队列一直等待,造成了内存回收系统的崩溃。GC对处于F-Queue中的对象进行第二次被标记,这时,该对象将被移除”即将回收”集合,等待回收。
类加载器按照层次,从顶层到底层,分为以下三种:
1)启动类加载器(Bootstrap ClassLoader)
负责将存放在JAVA_HOME/lib下的或被-Xbootclasspath参数所指定路径中的,并且是虚拟机识别的类库加载到虚拟机内存中。
注意的是启动类加载器无法被Java程序直接引用。
2)扩展类加载器(Extension ClassLoader)
负责加载JAVA_HOME/lib/ext目录中的或者被java.ext.dirs系统变量所指定的路径中的所有类库。
注意的是开发者可以直接使用扩展类加载器。
3)应用程序类加载器(Application ClassLoader)
ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。
负责加载用户类路径Classpath上所指定的类库,可直接使用这个加载器,如果应用程序没有自定义自己的类加载器,一般情况下该加载器就是程序中默认的类加载器。
Java提供了一种叫做对象序列化的机制,它把对象表示成一连串的字节,里面包含了对象的数据,对象的类型信息,对象内部的数据的类型信息等等。
因此,序列化可以看成是为了把对象存储在磁盘上或者是从磁盘上读出来并重建对象而把对象扁平化的一种方式。
反序列化是把对象从扁平状态转化成活动对象的相反的步骤。
答案是可以的,但是不能被加载使用。
这个主要是因为加载器的委托机制,在类加载器的结构图中,BootStrap是顶层父类,ExtClassLoader是BootStrap类的子类,ExtClassLoader是AppClassLoader的父类。当使用java.lang.String类时,Java虚拟机会将java.lang.String类的字节码加载到内存中。
加载某个类时,优先使用父类加载器加载需要使用的类。加载自定义java.lang.String类,其使用的加载器是AppClassLoader,根据优先使用父类加载器原理,AppClassLoader加载器的父类为ExtClassLoader,这时加载String使用的类加载器是ExtClassLoader,但类加载器ExtClassLoader在jre/lib/ext目录下并不能找到自定义java.lang.String类。
然后使用ExtClassLoader父类的加载器BootStrap,父类加载器BootStrap在JRE/lib目录的rt.jar找到了String.class,将其加载到内存中。
Java中有4种引用类型。
引用类型的级别和强度由高到低依次为:强引用->软引用->弱引用->虚引用。
引用类型 | 回收时间 | 使用说明 | 终止时间 |
---|---|---|---|
强引用 | 从来不会 | 对象的一般状态 | JVM停止运行时终止 |
软引用 | 当内存不足时 | 对象缓存 | 内存不足时终止 |
弱引用 | 正常垃圾回收时 | 对象缓存 | 垃圾回收后终止 |
虚引用 | 正常垃圾回收时 | 跟踪对象的垃圾回收状态 | 垃圾回收后终止 |
为了解决引用计数法的循环引用问题, Java 使用了可达性分析的方法。
通过一系列的“GC roots”对象作为起点搜索。如果在“GC roots”和一个对象之间没有可达路径,则称该对象是不可达的。
需要注意的是不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。
GC最基础的算法有三种:标记-清除算法、复制算法、标记-压缩算法,我们常用的垃圾回收器一般都采用分代收集算法。
标记-清除算法,“标记-清除”(Mark-Sweep)算法,如它的名字一样,算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象。
复制算法,“复制”(Copying)的收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
标记-压缩算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
分代收集算法,“分代收集”(Generational Collection)算法,把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
常见的 GC 日志开启参数包括:
1、 -Xloggc:filename,指定日志文件路径
2、 -XX:+PrintGC,打印 GC 基本信息
3、 -XX:+PrintGCDetails,打印 GC 详细信息
4、 -XX:+PrintGCTimeStamps,打印 GC 时间戳
5、 -XX:+PrintGCDateStamps,打印 GC 日期与时间
6、 -XX:+PrintHeapAtGC,打印 GC 前后的堆、方法区、元空间可用容量变化
7、 -XX:+PrintTenuringDistribution,打印熬过收集后剩余对象的年龄分布信息,有助于 MaxTenuringThreshold 参数调优设置
8、 -XX:+PrintAdaptiveSizePolicy,打印收集器自动设置堆空间各分代区域大小、收集目标等自动调节的相关信息
9、 -XX:+PrintGCApplicationConcurrentTime,打印 GC 过程中用户线程并发时间
10、 -XX:+PrintGCApplicationStoppedTime,打印 GC 过程中用户线程停顿时间
11、 -XX:+HeapDumpOnOutOfMemoryError,堆 oom 时自动 dump
12、 -XX:HeapDumpPath,堆 oom 时 dump 文件路径
Java 9 JVM 日志模块进行了重构,参数格式发生变化,这个需要知道。
GC 日志输出的格式,会随着上面的参数不同而发生变化。关注各个分代的内存使用情况、垃圾回收次数、垃圾回收的原因、垃圾回收占用的时间、吞吐量、用户线程停顿时间。
借助工具可视化工具可以更方便的分析,在线工具 GCeasy;离线版可以使用 GCViewer。
如果现场环境不允许,可以使用 JDK 自带的 jstat 工具监控观察 GC 情况。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。