方法区

栈、堆、方法区的交互关系

运行时数据结果图
在这里插入图片描述
在这里插入图片描述
栈、堆、方法区的交互关系
在这里插入图片描述

方法区的理解

方法区官方文档

在这里插入图片描述
在这里插入图片描述

/**
 * 测试设置方法区大小参数的默认值
 * <p>
 * jdk7及以前:
 * -XX:PermSize=100m -XX:MaxPermSize=100m
 * <p>
 * jdk8及以后:
 * -XX:MetaspaceSize=100m  -XX:MaxMetaspaceSize=100m
 * <p>
 * 设置堆内存大小
 * -Xms60m -Xmx60m
 *
 * @author shkstart  shkstart@126.com
 * @create 2020  12:16
 */
public class MethodAreaDemo {
    public static void main(String[] args) {
        System.out.println("start...");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

查看 jvisualvm 的gc内存堆大小情况
在这里插入图片描述
查看 jvisualvm 的类加载情况
在这里插入图片描述

Hotpt中方法区的演进
在这里插入图片描述
方法区概述
在这里插入图片描述
Hotspot 中方法区的演进
在这里插入图片描述

设置方法区大小与OOM

在这里插入图片描述
查看运行类的进程
jps
查看 jvm 参数是否有效(1.8的环境下)
jinfo -flag PermSize 进程号
jinfo -flag MaxPermSize 进程号
jvm参数文档

在这里插入图片描述

/**
 * 测试设置方法区大小参数的默认值
 * <p>
 * jdk7及以前:
 * -XX:PermSize=100m -XX:MaxPermSize=100m
 * <p>
 * jdk8及以后:
 * -XX:MetaspaceSize=100m  -XX:MaxMetaspaceSize=100m
 * <p>
 * -Xms60m -Xmx60m
 *
 * @author shkstart  shkstart@126.com
 * @create 2020  12:16
 */
public class MethodAreaDemo {
    public static void main(String[] args) {
        System.out.println("start...");
        try {
            Thread.sleep(1000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

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

方法区 OOM 演示

/**
 * jdk6/7中:
 * -XX:PermSize=10m -XX:MaxPermSize=10m
 *
 * jdk8中:
 * -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
 *
 * @author shkstart  shkstart@126.com
 * @create 2020  22:24
 */
public class OOMTest extends ClassLoader {
    public static void main(String[] args) {
        int j = 0;
        try {
            OOMTest test = new OOMTest();
            for (int i = 0; i < 10000; i++) {
                //创建ClassWriter对象,用于生成类的二进制字节码
                ClassWriter classWriter = new ClassWriter(0);
                //指明版本号,修饰符,类名,包名,父类,接口
                classWriter.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);
                //返回byte[]
                byte[] code = classWriter.toByteArray();
                //类的加载
                test.defineClass("Class" + i, code, 0, code.length);//Class对象
                j++;
            }
        } finally {
            System.out.println(j);
        }
    }
}

异常 信息:

8531
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
	at com.atguigu.java.OOMTest.main(OOMTest.java:29)

Process finished with exit code 1

如何解决OOM
在这里插入图片描述

方法区的内部结构

在这里插入图片描述
字符串常量 在jdk不同的版本中存放的位置不一样

方法区存储什么(规范版本)
在这里插入图片描述
类型信息包含域信息和方法信息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

查看class 字节码的反编译

/**
 * 测试方法区的内部构成
 * @author shkstart  shkstart@126.com
 * @create 2020  23:39
 */
public class MethodInnerStrucTest extends Object implements Comparable<String>,Serializable {
    //属性
    public int num = 10;
    private static String str = "测试方法的内部结构";
    //构造器
    //方法
    public void test1(){
        int count = 20;
        System.out.println("count = " + count);
    }
    public static int test2(int cal){
        int result = 0;
        try {
            int value = 30;
            result = value / cal;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public int compareTo(String o) {
        return 0;
    }
}

javap -v -p MethodInnerStrucTest.class > test.txt
方法区会记录class loader 的信息的,但是 test.txt中是没有class loader信息,因为class loader 存在于内存中

## 类型信息
public class com.atguigu.java.MethodInnerStrucTest extends java.lang.Object
    implements java.lang.Comparable<java.lang.String>, java.io.Serializable


  ## 域信息
  public int num;
    descriptor: I
    flags: ACC_PUBLIC

  private static java.lang.String str;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC

  ## 方法信息
  public void test1();
    ## 方法返回类型信息
    descriptor: ()V
    ## 方法的权限
    flags: ACC_PUBLIC
    ## code 字节码
    Code:
      ## stack 栈的最大深度为 3
      ## locals 局部量表数组长度为 2
      ## args_size 方法的参数个数为 1
      stack=3, locals=2, args_size=1
         0: bipush        20
         2: istore_1
         3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: new           #4                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
        13: ldc           #6                  // String count =
        15: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        18: iload_1
        19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        22: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        25: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: return
      LineNumberTable:
        line 17: 0
        line 18: 3
        line 19: 28
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      29     0  this   Lcom/atguigu/java/MethodInnerStrucTest;
            3      26     1 count   I

在这里插入图片描述

public class MethodAreaTest {
    public static void main(String[] args) {
        Order order = null;
        order.hello(); // 静态方法及时没有实例也是可以访问的,不会报错
        System.out.println(order.count);
    }
}

class Order {
    public static int count = 1;
    public static final int number = 2;


    public static void hello() {
        System.out.println("hello!");
    }
}

在这里插入图片描述
通过字节码文件可以看出,申明为final 的在编译时,值就已经分配配了,而static则没有分配
在这里插入图片描述

运行时常量池 & 常量池

先要能懂字节码中的常量池,才能能明白运行时常量池
在这里插入图片描述
常量池含义

在这里插入图片描述

为什么需要常量池

在这里插入图片描述
常量池中有什么?
在这里插入图片描述
在这里插入图片描述

运行时常量池

在这里插入图片描述

方法区使用举例

public class MethodAreaDemo {
    public static void main(String[] args) {
        int x = 500;
        int y = 100;
        int a = x / y;
        int b = 50;
        System.out.println(a + b);
    }
}

在这里插入图片描述
在这里插入图片描述
当方法不是静态方法时,本地变量表 0 的位置放的就是 this
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方法区的演变细节

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

永久代为什么要被元空间替换

  1. 不容易确定永久代的大小
  2. 永久代不容易调优
    在这里插入图片描述
    在这里插入图片描述
    StringTable(字符串常量表)为什么要移出永久代
    因为开发中会有大量字符串被创建,如果字符串保存在永久代,那么只有执行Full GC时才会对字符串进行回收,回收效率低。放在堆中,能及时回收内存。
/**
 * 结论:
 * 静态引用对应的对象实体始终都存在堆空间
 *
 * jdk7:
 * -Xms200m -Xmx200m -XX:PermSize=300m -XX:MaxPermSize=300m -XX:+PrintGCDetails
 * jdk 8:
 * -Xms200m -Xmx200m -XX:MetaspaceSize=300m -XX:MaxMetaspaceSize=300m -XX:+PrintGCDetails
 * @author shkstart  shkstart@126.com
 * @create 2020  21:20
 */
public class StaticFieldTest {
    private static byte[] arr = new byte[1024 * 1024 * 100];//100MB

    public static void main(String[] args) {
        System.out.println(StaticFieldTest.arr);

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

jdk1.8 测试
从gc日志上可以看到 arr 引用对应的对象实体放在堆空间中
而其他jdk版本静态引用对应的对象实体始终都存在堆空间(jdk1.6,jdk1.7,jdk1.8 等 ),而 静态变量如 arr 在不同的jdk版本中存放的位置是不一样的。
在这里插入图片描述
JHSDK 工具在 jdk1.9版本的 bin 目录中
在这里插入图片描述

/**
 * 《深入理解Java虚拟机》中的案例:
 * staticObj、instanceObj、localObj 变量本身存放在哪里?
 * @author shkstart  shkstart@126.com
 * @create 2020  11:39
 */
public class StaticObjTest {
    static class Test {
        static ObjectHolder staticObj = new ObjectHolder(); // 静态类属性
        ObjectHolder instanceObj = new ObjectHolder(); // 类属性

        void foo() {
            ObjectHolder localObj = new ObjectHolder(); // 局部变量
            System.out.println("done");
        }
    }

    private static class ObjectHolder {
    }

    public static void main(String[] args) {
        Test test = new StaticObjTest.Test();
        test.foo();
    }
}

在这里插入图片描述
在这里插入图片描述

方法区的垃圾回收

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

在这里插入图片描述


版权声明:本文为xyz9353原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。