文章目录
- 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