JVM

2022-05-09 09:50:58 浏览数 (1)

文章目录

  • Java脑图
    • 谈谈对Java的理解
  • 平台无关性
    • 特点
    • 类测试complie
    • 编译运行
    • 反编译
    • 不同系统解析class文件成为不同机器码
    • 为什么不直接编译成机器码
  • JVM如何加载.class文件
    • Native
  • 谈谈反射
    • 理解
    • 代码效果
    • 代码
      • Robot
      • ReflectSample
  • 谈谈ClassLoader
    • 类从编译到执行的过程
    • 谈谈ClassLoader
    • 追一下源码
    • ClassLoader种类
    • BootStrap
    • Ext
    • App
    • 自定义
    • 代码实现效果
    • 代码
      • ZhangSan.java
      • MyClassLoader
      • MyClassLoaderChecker
    • 使用场景展望
  • 类加载器双亲委派机制
    • 跟源码
      • 理解调用
      • 如何确定层级调用关系的?
      • 还是不信调用C ?
    • 为啥要用双亲委派机制去加载类?
  • 你了解Java的内存模型吗?
    • 理解
    • 内存模型
    • 程序计数器
    • 虚拟机栈
      • 口语指令分析代码
      • 递归为什么会引发异常1
      • 异常2
    • 本地方法栈
    • 元空间与永久代区别
    • 堆(Heap)
  • JVM存储角度
    • 三大性能调优参数-Xms -Xmx -Xss含义
    • Java内存模型中堆和栈的区别
      • 内存分配策略
      • 堆和栈的区别
    • 元空间、堆、线程、独占部分间的联系-内存角度
    • 不同jdk的intern()方法区别-jdk6 VS jkd6
      • 重现jdk6永久代内存异常
      • 对比不同jdk的intern()

Java脑图

谈谈对Java的理解

语言特性 泛型、反射、Lamda表达式 面向对象 封装、继承、多态 类库 集合、并发库、io、网络、NIO 异常处理

平台无关性

特点

编译 生成.class 二进制文件 javac xxx.java 运行 java xxx 命令反编译帮助 javap -help 反汇编 javap -c xxx

类测试complie

代码语言:javascript复制
/**
 * @Author bennyrhys
 * @Date 2020-03-23 23:38
 */
public class complie {
    public static void main(String[] args) {
        int i = 5, j = 6;
        i  ;
          j;
        System.out.println(i);
        System.out.println(j);
    }
}

编译运行

代码语言:javascript复制
(base) bennyrhysdeMacBook-Pro:src bennyrhys$ javac complie.java 
(base) bennyrhysdeMacBook-Pro:src bennyrhys$ java complie
6
7

反编译

代码语言:javascript复制
(base) bennyrhysdeMacBook-Pro:src bennyrhys$ javap -help
用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?        输出此用法消息
  -version                 版本信息
  -v  -verbose             输出附加信息
  -l                       输出行号和本地变量表
  -public                  仅显示公共类和成员
  -protected               显示受保护的/公共类和成员
  -package                 显示程序包/受保护的/公共类
                           和成员 (默认)
  -p  -private             显示所有类和成员
  -c                       对代码进行反汇编
  -s                       输出内部类型签名
  -sysinfo                 显示正在处理的类的
                           系统信息 (路径, 大小, 日期, MD5 散列)
  -constants               显示最终常量
  -classpath <path>        指定查找用户类文件的位置
  -cp <path>               指定查找用户类文件的位置
  -bootclasspath <path>    覆盖引导类文件的位置
(base) bennyrhysdeMacBook-Pro:src bennyrhys$ javap -c complie
Compiled from "complie.java"
public class complie {
  public complie();
  // 构造函数
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5 // 常量1栈顶
       1: istore_1 // 将栈顶放到局部变量1
       2: bipush        6
       4: istore_2
       5: iinc          1, 1
       8: iinc          2, 1
      11: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      14: iload_1
      15: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      18: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      21: iload_2
      22: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
      25: return
}
(base) bennyrhysdeMacBook-Pro:src bennyrhys$ 

不同系统解析class文件成为不同机器码

链接远程 ssh root@39.106.75.223 上传本地文件

创建相同包目录 mkdir -p 目录 复制文件 cp 文件 指定目录

为什么不直接编译成机器码

准备工作:无需重复校验语法 可扩展:字节码可由不同语言生成

JVM如何加载.class文件

Native

native接口

谈谈反射

理解

代码效果

代码

Robot

代码语言:javascript复制
package reflect;

/**
 * @Author bennyrhys
 * @Date 2020-03-24 16:31
 * 反射-机器人
 */
public class Robot {
    private String name;

    public void sayHi(String helloSentence) {
        System.out.println(helloSentence name);
    }

    private String throwHello(String tag) {
        return "Hello"   tag;
    }
}

ReflectSample

代码语言:javascript复制
package reflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @Author bennyrhys
 * @Date 2020-03-24 16:35
 * 反射就是把类中的各个成分,映射成Java对象,Class,Method,Field
 */
public class ReflectSample {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        // 获取类class,使用全路径,抛异常
        Class<?> rc = Class.forName("reflect.Robot");
        // 创建实例
        // 由于 public T newInstance() 返回值范型,需要强转,抛异常
        Robot o = (Robot) rc.newInstance();

        System.out.println("Class name is:" rc.getName());

        // 私有的只有反射可以获取到
        // getDeclaredMethod 获取到所有的方法,但是不能获取继承的方法,接口实现的方法
        Method getHello = rc.getDeclaredMethod("throwHello", String.class);
        // 默认false私有的,设置可访问私有的,否则报错
        getHello.setAccessible(true);
        // 调用私有方法. 对象实例,传入参数
        Object str = getHello.invoke(o, "Bennyrhys");
        System.out.println("getHello Private resulet is:"  str);

        // 调用共有方法,另一种方法【只能调用pubic方法,和继承方法,和实现方法】
        Method sayHi = rc.getMethod("sayHi", String.class);
        sayHi.invoke(o, "Welcome");

        // 获取私有属性
        Field name = rc.getDeclaredField("name");
        name.setAccessible(true);
        name.set(o, "Bennyrhys");

        sayHi.invoke(o, "Welcome");
    }
}

谈谈ClassLoader

类从编译到执行的过程

谈谈ClassLoader

追一下源码

抽象类

最重要的方法:给定类名去加载一个类

parent成员变量

属于ClassLoader,由此可见有多种类

ClassLoader种类

BootStrap

JVM核心库,C 编写

Ext

继承制URL

通过路径获取文件 文件用到才加载

代码语言:javascript复制
 // 获取ExtClassLoad加载路径
        System.out.println(System.getProperty("java.ext.dirs"));

Extensions

/Users/bennyrhys/Library/Java/Extensions:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java

App

代码语言:javascript复制
  // 获取AppClassLoader的classPath
        System.out.println(System.getProperty("java.class.path"));

/Users/bennyrhys/Documents/Idea_Demo/test/out/production/test 会到此路径找我们的class进行加载

/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/charsets.jar:/L ibrary/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/lib/tools.jar:/Users/bennyrhys/Documents/Idea_Demo/test/out/production/test:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar

(base) bennyrhysdeMacBook-Pro:reflect bennyrhys$ pwd

/Users/bennyrhys/Documents/Idea_Demo/test/out/production/test/reflect

(base) bennyrhysdeMacBook-Pro:reflect bennyrhys$ ls

ReflectSample.class Robot.class

自定义

根据名称或者位置寻找文件 进行解析,文件数据格式,,返回class对象

找类

抛出类找不到异常

可以在此处定义如何找到类 重新定义类

代码实现效果

代码

ZhangSan.java

代码语言:javascript复制
/**
 * @Author bennyrhys
 * @Date 2020-03-24 19:48
 */
public class ZhanSan {
    static {
        System.out.println("Hello ZhanSan");
    }
}

MyClassLoader

代码语言:javascript复制
package reflect;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * 自定义类加载
 * @Author bennyrhys
 * @Date 2020-03-24 19:54
 */
public class MyClassLoader extends ClassLoader{
    private String path;
    private String classLoaderName;


    public MyClassLoader(String path, String classLoaderName) {
        this.path = path;
        this.classLoaderName = classLoaderName;
    }

    // 用于寻找类文件
    @Override
    public Class findClass(String name) throws ClassNotFoundException {
        byte[] b = loadclassName(name);
        return defineClass(name, b, 0, b.length);
    }

    // 用于加载类文件
    private byte[] loadclassName(String name) {
        name = path   name   ".class";
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = new FileInputStream(new File(name));
            out = new ByteArrayOutputStream();
            int i = 0;
            while ((i = in.read()) != -1) {
                out.write(i);
            }
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
                in.close();
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        return out.toByteArray();
    }
}

MyClassLoaderChecker

代码语言:javascript复制
package reflect;

/**
 * @Author bennyrhys
 * @Date 2020-03-24 20:14
 */
public class MyClassLoaderChecker {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        MyClassLoader m = new MyClassLoader("/Users/bennyrhys/Desktop/jquery/", "myClassLoad");
        Class zhanSan = m.loadClass("ZhanSan");
        System.out.println(zhanSan.getClassLoader());
        zhanSan.newInstance();
    }
}

使用场景展望

自定义类加载 从网络中获取标准二进制流,进行类加载 可以对敏感的.class加密,在findClass里面解密 对生成的二进制流添加一些类信息 甚至思考一下AOP用这种方法实现

JAVA字节码增强技术之ASM ASM是一款基于java字节码层面的代码分析和修改工具;无需提供源代码即可对应用嵌入所需debug代码,用于应用API性能分析,代码优化和代码混淆等工作。ASM的目标是生成,转换和分析已编译的java class文件,可使用ASM工具读/写/转换JVM指令集。 ASM工具提供两种方式来产生和转换已编译的class文件,它们分别是基于事件和基于对象的表示模型。其中,基于事件的表示模型使用一个有序的事件序列表示一个class文件,class文件中的每一个元素使用一个事件来表示,比如class的头部,变量,方法声明JVM指令都有相对应的事件表示,ASM使用自带的事件解析器能将每一个class文件解析成一个事件序列。而基于对象的表示模型则使用对象树结构来解析每一个文件。

类加载器双亲委派机制

多种ClassLoader相互协作

跟源码

理解调用

loadClass

如何确定层级调用关系的?

按理来说parent都是URL,但为啥层级实际关系不是这样

但实际上是有层级的

代码语言:javascript复制
// 查看parent的
        System.out.println(zhanSan.getClassLoader().getParent()); // app
        System.out.println(zhanSan.getClassLoader().getParent().getParent()); // ext
        System.out.println(zhanSan.getClassLoader().getParent().getParent().getParent()); // c  

reflect.MyClassLoader@1540e19d

sun.misc.LauncherAppClassLoader@18b4aac2sun.misc.LauncherExtClassLoader@14ae5a5

null

还是不信调用C ?

为啥要用双亲委派机制去加载类?

你了解Java的内存模型吗?

理解

进程受限

进程受限于操作系统提供的可寻址空间。 可寻址空间由操作系统的位数决定 32位 4G

地址空间划分

内核空间 操作系统程序,c运行,链接计算机硬件,提供联网,虚拟内存 可以访问所有内存 用户空间 java实际运行

32位 进程最大访问3G 64位 进程最大访问512G

内存模型

中间核心部分

Java运行在虚拟机,运行时,会划分不同数据区域,方便内存空间方便管理

C编译器在划分内存的时候 数据段: 堆、栈、静态数据区 代码段:

程序计数器

虚拟机栈

栈的内存不需要gc,移除栈帧自动释放

口语指令分析代码

口语化指令

递归为什么会引发异常1

异常2

当虚拟机栈可以动态扩展时,无法申请足够多的内存 容易导致系统假死

本地方法栈

元空间与永久代区别

jdk8以后把元数据数据放到本地堆内存叫元空间 该区域在jdk7及以前属于永久代 元空间和永久代都是用来存储Class信息 (包括class的Methed和Field等) 元空间和永久代均是方法区的实现,只是实现有所不同。 方法区是JVM的一种规范 jdk1.7之后位于方法区的字符串常量池,已被移动到了Java堆中 jdk1.8中元空间替代了永久代 解决了运行空间不足可能产生的异常

堆(Heap)

线程共享的堆 存储对象实例 可以处在物理上不连续的空间,逻辑连续即可,可扩展的

JVM存储角度

三大性能调优参数-Xms -Xmx -Xss含义

调整JVM,堆、线程,所占内存的大小

Java内存模型中堆和栈的区别

内存分配策略

堆和栈的区别

通过栈中的引用变量访问堆中的地址 栈存储堆中首地址(引用变量) 到其作用域之外后释放 堆,数组首地址、实例对象 占据的内存不会被释放。只有在没有引用变量之后才会被视为垃圾回收,不确定的时间被垃圾回收机制释放掉

元空间、堆、线程、独占部分间的联系-内存角度

不同jdk的intern()方法区别-jdk6 VS jkd6

重现jdk6永久代内存异常

切换到1.7、1.8

对比不同jdk的intern()

1.7

1.6

0 人点赞