标记是从gcroot根节点开始标记: 根节点一般是:static引用的对象,常量引用的对象。虚拟机栈引用的对象,本地方法栈引用的对象
-
标记-清除,标记后在清除(会产生大量碎片)
-
标记-整理,标记后整理,像一端移动。
-
复制,把存活的对象复制进另一个内存,当对象存活比较多时,会产生大量的复制并浪费内存(一般新生代用这个。新生代存活的对象比较少。) -XX:SurvivorRatio用于eden和Survivor的比例配置。
cms
-
初始标记(stop the world,标记与gcroot直接关联的对象。)
-
并发标记(和用户线程一起运行)
-
重新标记(stop the world用于并发标记阶段,由于用户线程导致改变的那一部分对象)
-
并发清除 缺点
-
占用cpu资源
-
由于并发清理阶段,可能会导致浮动垃圾(用户线程运行导致的垃圾),无法处理,也是由于并发清理,要给用户线程留一定的内存。当内存不够用时候(current mode failure),会使用serial old来清理,单线程stop the world。导致性能降低
-
基于标记-清理的算法。会导致大量的空间碎片。可能导致提前触发 full gc (+UseCmsCompactAtFullCollection)开启合并整理
G1
优点
-
不会产生大量碎片
-
建立在可预测时间的回收模型 (g1把堆的划分多个大小相等的独立区域region,判断每个region的回收时间和回收大小。维护成一个优先列表,在规定的时间内,回收最大的region)
-
region之间的引用信息是通过remembered set来维护的。每个region都关联一个remembered set列表,对引用进行写操作时,判断是否是同一个region,如果不是。就在被引用对象的remembered set加入引用信息(如:a对b进行写操作。判断不是同一个region 就加入到b的remembered set里面。清理的时候就可以避免扫描全堆)
步骤
-
初始标记(stop the world)
-
并发标记(并发标记)
-
重新标记(并行标记)
-
回收(对region的回收价值和成本进行排序,在规定的时间内,制定回收计划,并行回收) serial单线程垃圾回收。新生代采用复制算法。老年代采用标记整理 parnew (serial的多线程版本)垃圾回收,不一定比单线程块。
类加载流程:加载->验证->准备(给静态变量默认值(static))——>链接->初始化(分配内存并初始化零值,不包含对象头)-使用。 类初始化的五个场景:
-
操作静态对象
-
new关键字
-
反射
-
加载main方法
加载:
java如何确定一个类是否相等
-
类的类空间名称相同
-
类加载机制相同
必须保证这两点。才能保证类相等
分析下列两种代码:
public class MyClassLoader {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = new ClassLoader() {
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream in = getClass().getResourceAsStream(fileName);
if (in == null) {
return super.loadClass(name);
}
byte[] b = new byte[in.available()];
in.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException();
}
}
};
Object finalizeEscapeGc = classLoader.loadClass("com.hoolai.load.MyClassLoader").newInstance();
System.out.println(finalizeEscapeGc.getClass());
System.out.println(finalizeEscapeGc instanceof com.hoolai.load.MyClassLoader);
}
}
out::put
class com.hoolai.load.MyClassLoader
false
__________________________________________________
public class MyClassLoader {
public static void main(String[] args) throws Exception {
ClassLoader classLoader = new ClassLoader() {
@Override
**public Class<?> findClass(String name) throws ClassNotFoundException {**
try {
String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
InputStream in = getClass().getResourceAsStream(fileName);
if (in == null) {
return super.loadClass(name);
}
byte[] b = new byte[in.available()];
in.read(b);
return defineClass(name, b, 0, b.length);
} catch (IOException e) {
throw new ClassNotFoundException();
}
}
};
Object finalizeEscapeGc = classLoader.loadClass("com.hoolai.load.MyClassLoader").newInstance();
System.out.println(finalizeEscapeGc.getClass());
System.out.println(finalizeEscapeGc instanceof com.hoolai.load.MyClassLoader);
}
}
out::put
class com.hoolai.load.MyClassLoader
true
这里不得不引入双亲委派模型 1、启动类加载器:加载<JAVA_HOME>lib/下面的类或者被-Xbootclasspath指定的路径。而且必须保证名称合法。 2、扩展类加载器:加载<JAVA_HOME>lib/ext目录中的下面的类 3、应用程序加载器:加载用户所在路径的类。默认是用应用程序加载器 先看类加载继承关系图
1、类加载机制代码如下:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
1、检查是否被加载 2、没有被加载的话,且存在父类使用父类加载 3、否则调用findClass加载。 分析第一段代码: 重载了loadClass方法,并且当自定义加载器获取不到这个类时,才使用父类加载 即存在两个MyClassLoader对象。一个是使用自定义加载器加载,一个是使用应用程序加载器加载(main调用的时候) 第二段代码: 重载了findClass 判断类没有被加载。先调用夫类加载。即被应用程序加载器加载。所以两个类相等 这就是双亲委派机制。给予了java最基本的保障。即使不同的加载器加载object对象,都是由最顶层的类加载。