第五章 堆内存


文章目录

  • 前言
  • 一、 本地方法
    • 1、什么是本地方法
    • 2、为什么使用 Native Method
  • 二、 本地方法栈
  • 三、 堆内存
    • 1、-Xms10m -Xmx10m 设置堆内存的大小
    • 2、内存细分
    • 3、设置堆空间的大小与OOM
      • jps 查看Java程序进程
      • jstat -gc 进程号 查看堆内存参数
      • -XX:+PrintGCDetails 设置虚拟机参数,将堆内存情况打印到控制台
    • OutOfMemoryError 内存溢出
    • 4、年轻代与老年代
      • 新生代与老年代的参数设置(一般不会调)
      • -XX:-UseAdaptiveSizePolicy 关闭年轻代中伊甸园区和幸存者区的自适应比例
      • -XX:SurvivorRatio=8 设置年轻代中伊甸园区和幸存者区的内存比例
      • -Xmn:设置新生代的空间的大小。 (一般不设置)
    • 5、图解对象分配过程
    • 6、对象分配的特殊情况
    • 7、常用的调优工具
  • 四、 GC垃圾回收
    • 1、MinorGC / MajorGC / FullGC 的对比
    • 2、测试垃圾回收
  • 五、堆空间的其他细节及总结
    • 1、堆空间的分代思想
    • 2、总结内存分配策略
    • 3、为对象分配内存 TLAB (伊甸园区)
    • 4、小结堆空间的参数设置
    • 5、堆是分配对象的唯一选择吗 ?
      • 逃逸分析
      • 栈上分配
      • 同步策略
      • 标量替换


前言


一、 本地方法

1、什么是本地方法

第五章 堆内存_第1张图片

package com.atguigu.java;

/**
 * @author shkstart
 * @create 2020 下午 8:53
 */
public class IHaveNatives {
    
    // 被 native 关键字修饰的就是本地方法,有方法体,但是不是Java代码实现的, 
    //native 关键字不能 和 abstract 关键字一起使用
    public native void Native1(int x);

    public native static long Native2();

    private native synchronized float Native3(Object o);

    native void Native4(int[] ary) throws Exception;

}


2、为什么使用 Native Method

第五章 堆内存_第2张图片
第五章 堆内存_第3张图片
第五章 堆内存_第4张图片

二、 本地方法栈

第五章 堆内存_第5张图片
第五章 堆内存_第6张图片
第五章 堆内存_第7张图片

三、 堆内存

第五章 堆内存_第8张图片
第五章 堆内存_第9张图片

1、-Xms10m -Xmx10m 设置堆内存的大小

测试 将下面两个类的堆空间大小分别设置 10mb 和 20 mb

package com.atguigu.java;

/**
 * -Xms10m -Xmx10m
 *
 * @author shkstart  [email protected]
 * @create 2020  16:41
 */
public class HeapDemo {
    public static void main(String[] args) {
        System.out.println("start...");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("end...");
    }

}

package com.atguigu.java;

/**
 * -Xms20m -Xmx20m
 * @author shkstart  [email protected]
 * @create 2020  16:42
 */
public class HeapDemo1 {
    public static void main(String[] args) {
        System.out.println("start...");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("end...");
    }
}

第五章 堆内存_第10张图片

第五章 堆内存_第11张图片

把上面两个代码跑起来,然后去JDK的安装目录找到,虚拟机监控运行起来。

第五章 堆内存_第12张图片
第五章 堆内存_第13张图片
第五章 堆内存_第14张图片
第五章 堆内存_第15张图片

2、内存细分

堆内存从逻辑上划分是三部分,年轻代,老年代,元空间。但是实际上元空间更偏向于方法区,所以堆空间我们还是看年轻代和老年代两部分。

第五章 堆内存_第16张图片

3、设置堆空间的大小与OOM

第五章 堆内存_第17张图片

package com.atguigu.java;

/**
 * 1. 设置堆空间大小的参数
 * -Xms 用来设置堆空间(年轻代+老年代)的初始内存大小
 *      -X 是jvm的运行参数
 *      ms 是memory start
 * -Xmx 用来设置堆空间(年轻代+老年代)的最大内存大小
 *
 * 2. 默认堆空间的大小
 *    初始内存大小:物理电脑内存大小 / 64
 *             最大内存大小:物理电脑内存大小 / 4
 * 3. 手动设置:-Xms600m -Xmx600m
 *     开发中建议将初始堆内存和最大的堆内存设置成相同的值。
 *
 * 4. 查看设置的参数:方式一: jps   /  jstat -gc 进程id
 *                  方式二:-XX:+PrintGCDetails
 * @author shkstart  [email protected]
 * @create 2020  20:15
 */
public class HeapSpaceInitial {
    public static void main(String[] args) {

        //返回Java虚拟机中的堆内存总量
        long initialMemory = Runtime.getRuntime().totalMemory() / 1024 / 1024;
        //返回Java虚拟机试图使用的最大堆内存量
        long maxMemory = Runtime.getRuntime().maxMemory() / 1024 / 1024;

        System.out.println("-Xms : " + initialMemory + "M");
        System.out.println("-Xmx : " + maxMemory + "M");

//        System.out.println("系统内存大小为:" + initialMemory * 64.0 / 1024 + "G");
//        System.out.println("系统内存大小为:" + maxMemory * 4.0 / 1024 + "G");

        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

jps 查看Java程序进程

第五章 堆内存_第18张图片

jstat -gc 进程号 查看堆内存参数

第五章 堆内存_第19张图片

-XX:+PrintGCDetails 设置虚拟机参数,将堆内存情况打印到控制台

OutOfMemoryError 内存溢出

设置堆大小

第五章 堆内存_第20张图片

执行程序

package com.atguigu.java;

import java.util.ArrayList;
import java.util.Random;

/**
 * -Xms600m -Xmx600m
 * @author shkstart  [email protected]
 * @create 2020  21:12
 */
public class OOMTest {
    public static void main(String[] args) {
        ArrayList<Picture> list = new ArrayList<>();
        while(true){
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            list.add(new Picture(new Random().nextInt(1024 * 1024)));
        }
    }
}

class Picture{
    private byte[] pixels;

    public Picture(int length) {
        this.pixels = new byte[length];
    }
}

堆内存拉满
第五章 堆内存_第21张图片
第五章 堆内存_第22张图片
第五章 堆内存_第23张图片

4、年轻代与老年代

第五章 堆内存_第24张图片

新生代与老年代的参数设置(一般不会调)

第五章 堆内存_第25张图片
第五章 堆内存_第26张图片
第五章 堆内存_第27张图片

-XX:-UseAdaptiveSizePolicy 关闭年轻代中伊甸园区和幸存者区的自适应比例

-XX:SurvivorRatio=8 设置年轻代中伊甸园区和幸存者区的内存比例

第五章 堆内存_第28张图片
第五章 堆内存_第29张图片

-Xmn:设置新生代的空间的大小。 (一般不设置)

5、图解对象分配过程

第五章 堆内存_第30张图片
第五章 堆内存_第31张图片
第五章 堆内存_第32张图片

伊甸园区满了会进行垃圾回收,幸存者放入 01 区,当幸存者过多时,01 区装不下仍然不会进行垃圾回收,而是直接让多出的幸存者进入老年代。

6、对象分配的特殊情况

第五章 堆内存_第33张图片

7、常用的调优工具

第五章 堆内存_第34张图片

四、 GC垃圾回收

1、MinorGC / MajorGC / FullGC 的对比

对于程序的调优,我们更多关注的是尽量减少 垃圾回收的次数,因为每次垃圾回都会让用户线程停止工作。

第五章 堆内存_第35张图片
第五章 堆内存_第36张图片

第五章 堆内存_第37张图片
第五章 堆内存_第38张图片

2、测试垃圾回收

字符串常量池以前在方法区,现在在堆空间

package com.atguigu.java1;

import java.util.ArrayList;
import java.util.List;

/**
 * 测试MinorGC 、 MajorGC、FullGC
 * -Xms9m -Xmx9m -XX:+PrintGCDetails
 * @author shkstart  [email protected]
 * @create 2020  14:19
 */
public class GCTest {
    public static void main(String[] args) {
        int i = 0;
        try {
            List<String> list = new ArrayList<>();
            String a = "atguigu.com";
            while (true) {
                list.add(a);
                a = a + a;
                i++;
            }

        } catch (Throwable t) {
            t.printStackTrace();
            System.out.println("遍历次数为:" + i);
        }
    }
}


五、堆空间的其他细节及总结

1、堆空间的分代思想

第五章 堆内存_第39张图片
第五章 堆内存_第40张图片

2、总结内存分配策略

第五章 堆内存_第41张图片
第五章 堆内存_第42张图片


package com.atguigu.java1;

/** 测试:大对象直接进入老年代
 * -Xms60m -Xmx60m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails
 * @author shkstart  [email protected]
 * @create 2020  21:48
 */
public class YoungOldAreaTest {
    public static void main(String[] args) {
        byte[] buffer = new byte[1024 * 1024 * 20];//20m

    }
}

3、为对象分配内存 TLAB (伊甸园区)

第五章 堆内存_第43张图片
第五章 堆内存_第44张图片
第五章 堆内存_第45张图片
第五章 堆内存_第46张图片
第五章 堆内存_第47张图片

4、小结堆空间的参数设置

第五章 堆内存_第48张图片
第五章 堆内存_第49张图片

5、堆是分配对象的唯一选择吗 ?

第五章 堆内存_第50张图片

逃逸分析

第五章 堆内存_第51张图片
第五章 堆内存_第52张图片
第五章 堆内存_第53张图片

第五章 堆内存_第54张图片

package com.atguigu.java2;

/**
 * 逃逸分析
 *
 *  如何快速的判断是否发生了逃逸分析,大家就看new的对象实体是否有可能在方法外被调用。
 * @author shkstart
 * @create 2020 下午 4:00
 */
public class EscapeAnalysis {

    public EscapeAnalysis obj;

    /*
    方法返回EscapeAnalysis对象,发生逃逸
     */
    public EscapeAnalysis getInstance(){
        return obj == null? new EscapeAnalysis() : obj;
    }
    /*
    为成员属性赋值,发生逃逸
     */
    public void setObj(){
        this.obj = new EscapeAnalysis();
    }
    //思考:如果当前的obj引用声明为static的?仍然会发生逃逸。

    /*
    对象的作用域仅在当前方法中有效,没有发生逃逸
     */
    public void useEscapeAnalysis(){
        EscapeAnalysis e = new EscapeAnalysis();
    }
    /*
    引用成员变量的值,发生逃逸
     */
    public void useEscapeAnalysis1(){
        EscapeAnalysis e = getInstance();
        //getInstance().xxx()同样会发生逃逸
    }
}

第五章 堆内存_第55张图片
第五章 堆内存_第56张图片

栈上分配

第五章 堆内存_第57张图片

package com.atguigu.java2;

/**
 * 栈上分配测试
 * -Xmx1G -Xms1G  -XX:+PrintGCDetails -XX:-DoEscapeAnalysis(关闭逃逸分析)  -XX:-DoEscapeAnalysis(开启逃逸分析)
 * @author shkstart  [email protected]
 * @create 2020  10:31
 */
public class StackAllocation {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        for (int i = 0; i < 10000000; i++) {
            alloc();
        }
        // 查看执行时间
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为: " + (end - start) + " ms");
        // 为了方便查看堆内存中对象个数,线程sleep
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }

    private static void alloc() {
        User user = new User();//未发生逃逸
    }

    static class User {

    }
}

同步策略

第五章 堆内存_第58张图片
第五章 堆内存_第59张图片

package com.atguigu.java2;

/**
 * 同步省略说明
 * @author shkstart  [email protected]
 * @create 2020  11:07
 */
public class SynchronizedTest {
    public void f() {
        Object hollis = new Object();
        synchronized(hollis) {
            System.out.println(hollis);
        }
    }
}

标量替换

第五章 堆内存_第60张图片
第五章 堆内存_第61张图片

package com.atguigu.java2;

/**
 * 标量替换测试
 *  -Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations
 * @author shkstart  [email protected]
 * @create 2020  12:01
 */
public class ScalarReplace {
    public static class User {
        public int id;
        public String name;
    }

    public static void alloc() {
        User u = new User();//未发生逃逸
        u.id = 5;
        u.name = "www.atguigu.com";
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; i++) {
            alloc();
        }
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为: " + (end - start) + " ms");
    }
}

/*
class Customer{
    String name;
    int id;
    Account acct;

}

class Account{
    double balance;
}


 */

第五章 堆内存_第62张图片

第五章 堆内存_第63张图片
第五章 堆内存_第64张图片

你可能感兴趣的:(JVM,java,jvm)