谈谈你对java的理解
- 平台无关性
- GC
- 语言特性
- 面向对象
- 类库
- 异常处理
平台无关如何实现
java源码首先被编译成字节码,再由不同平台的jvm进行解析,java语言在不同平台上运行时不需要重新编译,java虚拟机在执行字节码时,把字节码转换为具体平台上的机器指令
为什么jvm不直接将源码解析成机器码去执行
- 每次执行需要重新编译,进行语法检查语义分析等,性能就会受影响
- 别的语言也可以解析成字节码
jvm如何加载.class文件
jvm主要由ClassLoader,RuntimeDataArea,ExecutionEngine,NativeInterface这四部分组成,它通过classloader将符合特定格式的class文件加载到内存,再通过executionEngine对命令进行解析,并提交给操作系统执行
谈谈反射
写一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException { Class rc = Class.forName("com.test.basic.Robot"); Robot r = (Robot) rc.newInstance(); System.out.println("class name is :" + rc.getName());
Method getHello = rc.getDeclaredMethod("throwHello", String.class); getHello.setAccessible(true); Object str = getHello.invoke(r, "bob"); System.out.println("get hello result:" + str);
Method sayHi = rc.getMethod("sayHi", String.class); sayHi.invoke(r, "welcom");
Field name = rc.getDeclaredField("name"); name.setAccessible(true); name.set(r, "dd"); sayHi.invoke(r, "welcome");
}
|
类从编译到执行的过程
谈谈classloader
classloader的种类
自定义classloader的实现
关键函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| public class MyClassLoader extends ClassLoader {
private String path; private String classLoaderName;
public MyClassLoader(String path, String classLoaderName) { this.path = path; this.classLoaderName = classLoaderName; }
@Override protected Class findClass(String name) { byte[] b = loadClassData(name); return defineClass(name, b, 0, b.length); }
private byte[] loadClassData(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 (IOException e) { e.printStackTrace(); } finally { try { in.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } } return out.toByteArray(); }
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { MyClassLoader myClassLoader = new MyClassLoader("/home/zw/Documents/","Wali"); Class c = myClassLoader.loadClass("Wali"); System.out.println(c.getClassLoader()); c.newInstance();
}
|
谈谈类加载的双亲委派机制
为什么使用双亲委派机制
避免多份同样的字节码的加载
类加载的方式
- 隐式加载:new
- 显式加载:loadClass,forName等
loadClass和forName的区别
- 区别:forName得到的class是完成初始化的,loadClass得到的class还没有链接
1 2 3 4
| public static void main(String[] args) throws ClassNotFoundException { // ClassLoader cl = Robot.class.getClassLoader(); Class c = Class.forName("com.test.basic.Robot"); }
|
- forName用于需要执行初始化的类,loadClass用于延时加载,加快加载速度
Java的内存模型
内存简介
地址空间的划分
java的内存空间在用户空间
内存空间即RuntimeDataArea
程序计数器(Program Counter Register)
程序计数器是逻辑计数器,为了线程切换后都能恢复正确的执行位置,每个线程都有独立的程序计数器,并且只为java方法计数
java虚拟机栈(Stack)
虚拟机栈包含了单个线程每个方法执行的栈帧,栈帧存储了局部变量表,操作栈,动态连接,方法出口
局部变量表和操作数栈
局部变量表为操作数栈提供数据支撑
递归为什么会导致StackOverFlow异常
虚拟机栈过多会引发OutOfMemory异常
本地方法栈
与虚拟机栈类似,主要作用于标注了native的方法
在jdk7以后,把类的元数据放在本地堆内存中,这块区域叫MetaSpace,该区域在jdk7以前属于永久代,二者都是用来储存class的相关信息,包括class对象的method,filed.
元空间和永久代都是方法区的实现,方法区只是jvm的规范.
在java7之后,原先位于方法区的字符串常量池被移动到java堆中,并且在java8后使用元空间替换永久代.
元空间使用的本地内存,而永久代使用的是jvm的内存. 这样的好处是解决了空间不足,其大小是动态分配的
元空间内存空间使用的是本机内存,没有了字符串常量池(java7后被移动到了堆中),永久代的空间有限
java堆(Heap)
JVM三大性能调优参数-Xms -Xmx -Xss的含义
1
| java -Xms128m -Xmx128m -Xss256k -jar xxx.jar
|
- Xss:规定了每个线程虚拟机栈(堆栈)的大小(一般情况256k足够,将会影响并发线程数的大小)
- Xms:堆的初始值(一旦对象容量超过java堆的容量,会进行扩容)
- Xmx:堆能达到的最大值(一般情况设置成和xms一致,不会发生抖动)
java内存模型中堆和栈的区别
内存分配策略
联系
区别
- 管理方式:栈自动释放,堆需要GC
- 空间大小:栈比堆小
- 碎片相关:栈产生的碎片远小于堆
- 分配方式:栈支持静态和动态分配,而堆只支持动态分配
- 效率:栈的效率比堆高
元空间,堆,线程独占部分空间的联系-内存角度
不同JDK版本之间intern()方法的区别-JDK6 VS JDK6+
1 2
| String s = new String("a"); s.intern();
|
jdk6+不仅能在池中添加对象,还能在池中添加堆中的引用