JVM(第一部分JVM参数及四大引用)

274 阅读5分钟

Java8 JVM内存结构

基本结构与之前类似,只是Java8取消了之前的“永久代”,取而代之的是“元空间”——Metaspace,两者本质是一样的。“永久代”使用的是JVM的堆内存,而“元空间”是直接使用的本机物理内存

GC Roots

如何判断一个对象可以被回收?

引用计数算法

维护一个计数器,如果有对该对象的引用,计数器+1,反之-1。无法解决循环引用的问题

可达性分析算法

从一组名为“GC Roots”的根节点对象出发,向下遍历。那些没有被遍历到、与GC Roots形成通路的对象,会被标记为“回收”。

哪些对象可以作为GC Roots?

  • 1.虚拟机栈(栈帧中的局部变量)中引用的对象。
  • 2.本地方法栈(native)中引用的对象。
  • 3.方法区中常量引用的对象
  • 4.方法区中类静态属性引用的对象

JVM参数

JVM 三种类型参数

标配参数 比如-version-help-showversion等,几乎不会改变。

X参数

用得不多,比如-Xint,解释执行模式;-Xcomp,编译模式;-Xmixed,开启混合模式(默认)。

XX参数

重要,用于JVM调优。

JVM XX参数

布尔类型

公式:-XX:+某个属性-XX:-某个属性,开启或关闭某个功能。比如-XX:+PrintGCDetails,开启GC详细信息。

KV键值类型

公式-XX:属性key=值value。比如-XX:Metaspace=128m-XX:MaxTenuringThreshold=15

JVM Xms/Xmx参数

-Xms-Xmx十分常见,用于设置初始堆大小和最大堆大小。第一眼看上去,既不像X参数,也不像XX参数。实际上-Xms等价于-XX:InitialHeapSize-Xmx等价于-XX:MaxHeapSize。所以-Xms-Xmx属于XX参数。

JVM 查看参数

使用jps -l配合jinfo -flag JVM参数 pid 。先用jsp -l查看java进程,选择某个进程号。

17888 org.jetbrains.jps.cmdline.Launcher
5360 org.jetbrains.idea.maven.server.RemoteMavenServer
18052 demo3.demo3

jinfo -flag PrintGCDetails 18052可以查看18052 Java进程的PrintGCDetails参数信息。

-XX:-PrintGCDetails

查看所有参数

使用jps -l配合jinfo -flags pid可以查看所有参数。 也可以使用java -XX:+PrintFlagsInitial

查看修改后的参数

使用java -XX:PrintFlagsFinal可以查看修改后的参数,与上面类似。只是修改过后是:=而不是=。

查看常见参数

如果不想查看所有参数,可以用-XX:+PrintCommandLineFlags查看常用参数。

D:\java增强训练>java -XX:+PrintCommandLineFlags
-XX:InitialHeapSize=132301184 -XX:MaxHeapSize=2116818944 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressed
Oops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC

JVM常用参数

-Xmx/-Xms

最大和初始堆大小。最大默认为物理内存的1/4初始默认为物理内存的1/64

-Xss

等价于-XX:ThresholdStackSize。用于设置单个栈的大小,系统默认值是0,不代表栈大小为0。而是根据操作系统的不同,有不同的值。比如64位的Linux系统是1024K,而Windows系统依赖于虚拟内存。

-Xmn

新生代大小,一般不调。

-XX:MetaspaceSize

设置元空间大小。

-XX:+PrintGCDetails

输出GC收集信息,包含GC和Full GC信息。

-XX:SurvivorRatio

新生代中,Eden区和两个Survivor区的比例,默认是8:1:1。通过-XX:SurvivorRatio=4改成4:1:1

-XX:NewRatio

老生代和新年代的比列,默认是2,即老年代占2,新生代占1。如果改成-XX:NewRatio=4,则老年代占4,新生代占1。

-XX:MaxTenuringThreshold

新生代设置进入老年代的时间,默认是新生代逃过15次GC后,进入老年代。如果改成0,那么对象不会在新生代分配,直接进入老年代。

四大引用

以下Demo都需要设置-Xmx-Xms,不然系统默认很大,很难演示。

强引用

使用new方法创造出来的对象,默认都是强引用。GC的时候,就算内存不够,抛出OutOfMemoryError也不会回收对象,死了也不回收。详见StrongReferenceDemo。

public class StrongReferenceDemo {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();
        o1 = null;
        System.gc();
        System.out.println(o2);
        
    }
}

软引用

需要用Object.Reference.SoftReference来显示创建。如果内存够,GC的时候不回收。内存不够,则回收。常用于内存敏感的应用,比如高速缓存。详见SoftReferenceDemo。

public class SoftReferenceDemo {
    public static void main(String[] args) {
        softRef_Memory_Enough();
        System.out.println("Not Enough");
        softRef_Memory_NotEnough();
    }

    private static void softRef_Memory_Enough() {
        Object o1 = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());
        System.out.println("===========");
        o1 = null;
        System.gc();
        System.out.println(o1);
        System.out.println(softReference.get());
    }

    private static void softRef_Memory_NotEnough() {
        Object o1 = new Object();
        SoftReference<Object> softReference = new SoftReference<>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());
        System.out.println("===========");
        o1 = null;
        System.gc();

        try {
            byte[] bytes = new byte[30 * 1024 * 1024];
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println(o1);
            System.out.println(softReference.get());
        }
    }
}

弱引用

需要用Object.Reference.WeakReference来显示创建。无论内存够不够,GC的时候都回收,也可以用在高速缓存上。详见WeakReferenceDemo

public class WeakHashMapDemo {
    public static void main(String[] args) {
        myHashMap();
        System.out.println("===============");
        myWeakHashMap();
    }

    private static void myHashMap() {
        HashMap<Integer, String> map = new HashMap<>();
        Integer key = 1;
        String value = "HashMap";
        map.put(key, value);
        System.out.println(map);

        key = null;
        System.out.println(map);
        System.gc();
        System.out.println(map + "\t" + map.size());
    }

    private static void myWeakHashMap() {
        WeakHashMap<Integer, String> map = new WeakHashMap<>();
        Integer key = 2;
        String value = "WeakHashMap";
        map.put(key, value);
        System.out.println(map);

        key = null;
        System.out.println(map);

        System.gc();
        System.out.println(map + "\t" + map.size());
    }
}

虚引用

软引用和弱引用可以通过get()方法获得对象,但是虚引用不行。虚引形同虚设,在任何时候都可能被GC,不能单独使用,必须配合引用队列(ReferenceQueue)来使用。设置虚引用的唯一目的,就是在这个对象被回收时,收到一个通知以便进行后续操作,有点像Spring的后置通知。详见PhantomReferenceDemo。

public class PhantomReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        PhantomReference phantomReference = new PhantomReference(o1, referenceQueue);
        System.out.println(o1);
        System.out.println(phantomReference.get());
        System.out.println(referenceQueue.poll());

        System.out.println("===========");

        o1 = null;
        System.gc();
        Thread.sleep(500);
        System.out.println(o1);
        System.out.println(phantomReference.get());
        System.out.println(referenceQueue.poll());
    }
}

引用队列

弱引用、虚引用被回收后,会被放到引用队列里面,通过poll方法可以得到。关于引用队列和弱、虚引用的配合使用,见ReferenceQueueDemo。

public class ReferenceQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        WeakReference<Object> weakReference = new WeakReference<>(o1, referenceQueue);
        System.out.println(o1);
        System.out.println(weakReference.get());
        System.out.println(referenceQueue.poll());

        System.out.println("==========");
        o1 = null;
        System.gc();
        Thread.sleep(500);
        System.out.println(o1);
        System.out.println(weakReference.get());
        System.out.println(referenceQueue.poll());

    }
}