jvm:
谈谈对java的理解?
1.平台无关性
2.GC(垃圾回收机制)
3.语言特性
4.面向对象
5.类库
6.异常处理
compoile Once,Run Anywhere(一次打包,到处运行)如何实现?
编译时 (javac cc/ccc/bb.java)
编译后,行成一个 xxx.class文件
运行时 (java cc.ccc.bb)
如何查看 bb.class 中的内容
javap -help 可以查看一下指令
对 .class 文件进行反编译
javap -c cc.ccc.bb
流程:
使用 javac编译 .java 的文件 生成字节码 .class 文件
然后由JVM进行解析,转换成特定平台的执行指令(JVM For linux
,JVM for win,JVM for IOS)
为什么JVM不直接将源码解析成机器码去执行?
如果用源码去各个平台,在编译前都需要去检查
语法的错误,会做很多重复的事情
JVM如何加载.class文件?
JVM是内存中的虚拟机
java虚拟机:
Class Loader : 依据特定格式,加载class文件到内存
Execution Engine : 对命令进行解析
Native Interface : 融合不同开发语言的原生库为java所用
Runtime Data Area:JVM内存空间结构模型
谈谈反射?
java 反射机制是在运行状态中,对于任意一个类
,都能够知道这个类的所有属性和方法,对于任意
一个对象,都能够调用他的任意方法和属性,这种
这种动态获取信息以及动态调用对象方法的功能
称为java反射机制
反射例子:
Class rc = Class.foName("类的全路径"); --获取一个类对象
rc.newInstance(); -- 创建一个实例
rc.getName(); --名字
Method[] getHello = rc.getDeclaredMethods(); --获取类中的方法
Object aa = getHello.invoke(Object,"参数"); --调用方法
使用私有的方法前,必须加 getHello.setAccessible(true)
Fields name = rc.getDeclaredFields(); --获取属性
name.set() ---设置属性 使用私有属性前 name.setAccessible(true)
类从编译到执行的过程?
1.编译器将 aa.java 源文件 编译为 aa.class字节码文件
2.ClassLoader 将字节码转换为JVM 中的Class<aa>对象
3.JVM利用Class<aa>对象实例化为aa对象
谈谈ClassLoader?
ClassLoader 在JAVA中有着非常重要的作用,它主要工作
在Class装载的加载阶段,其主要作用是从系统外部获得
Class二进制数据流,他是JAVA的核心组件,所有的Class
都是由ClassLoader进行加载的,ClassLoader 负责通过
将Class文件里的二进制数据流装载进系统,然后交给JAVA
虚拟机进行连接、初始化等操作。
ClassLoader的种类?
BootStrapClassLoader : C++编写,加载核心库 java.*
ExtClassLoader : Java编写,加载扩展库 javax.*
AppClassLoader : Java编写,加载程序所在目录
自定义ClassLoader : Java编写,
(关键函数 findClass (用于寻找类文件,返回的是一个字节数组);
defineClass())
谈谈类加载的双亲委派机制?
加载类的过程 首先 由自定义的ClassLoader 去看是否曾经
加载过(通过findLoadedClass方法加载,然后判断是否为空),
如果有就加载,没有就向上,App -> Ext -> BootStrap,
如果都没有加载过,则由 BootStrap 去指定的路径下进行加载,
如果没有 依次向下 ->Ext -> App -> 自定义
为什么要使用双亲委派机制去加载类?
1.避免多份同样的字节码的加载
2.避免恶意修改源代码
类的加载方式?
隐式:new
显式加载:loadClass,forName 等
类的装载过程?
加载:
通过ClassLoader加载class文件字节码,生成Class对象
链接:
校验:检查加载的class的正确性和安全性
准备:为类变量(static)分配存储空间并设置类变量初始值
解析:JVM将常量池内的符号引用转换为直接引用
初始化:
执行类变量赋值和静态代码块
loadClass 和 forName 的区别
Class.forName 得到的class是已经初始化完成的
(Class r = Class.forName("com.aa");)
Class.loadClass 得到的class是还没有链接的 (没有链接与进行初始化)
(ClassLoader cl = aa.class.getClassLoader();)
作用:
1.在加载 jdbc 连接的时候用 Class.forName();
因为,com.mysql.jdbc.Driver 中有一段静态代码
需要初始化,所以要用forName才行,才能获得Driver
2.spring ioc 加载的时候,是lazy加载,为了加快加载速度
所以采用Class.loadClass(),在实际使用的时候在加载
jdk 8 java内存模型
从线程的角度看:
线程私有的:
程序计数器(是逻辑计数器,不会发生内存泄露);
(当前线程所执行的字节码行号指示器)
(改变计数器的值来选取下一条需要执行的字节码指令)
(和线程是一对一的关系即“线程私有”)
虚拟机栈:
(java方法执行的内存模型)
(包含多个栈帧
包含:
局部变量表:包含方法执行过程中的所有变量,为操作数栈提供必要的数据支撑
操作数栈,
动态连接,
返回地址)
本地方法栈:
(与虚拟机栈类似,主要作用于标注了native的方法)
所有线程共享的:
metaSpace(类加载信息OOM),
常量池,
堆
递归为什么会引发java.lang.StackOverflowError异常?
递归过深,栈帧(每调用一个方法,就会行成一个栈帧压入虚拟机栈中)数超过出虚拟栈深度
元空间(metaSpace)与永久代(permGen)的区别
(jdk 8) (jdk 7 以前)
首先,这两个都是用来存储Class 相关信息
元空间使用本地内存,而永久代使用的JVM的内存
MetaSpace 相比PermGen的优势
字符串常量池存在永久代中,容易出现性能问题和内存溢出
类和方法的信息大小难易确定,给永久代的大小指定带来困难
永久代会为GC带来不必要的复杂性
java堆(heap)
对象实例的分配区域
GC管理的主要区域
JVM三大性能调优参数-Xms -Xmx -Xss的含义
-Xss:规定了每个线程虚拟机栈(堆栈)的大小
(一般256就足够了)
-Xms:堆的初始值
-Xmx:堆能达到的最大值
(一般这两个值设置的是相同的,避免扩容时候
发生内存抖动,影响程序运行时的稳定性)
Java内存模型中堆和栈的区别--内存分配策略
内存分配策略:
静态存储:编译时确定每个数据目标在运行时的存储空间需求
栈式存储:数据区需求在编译时未知,运行时模块入口前确定
堆式存储:编译时或运行时模块入口都无法确定,动态分配
堆与栈的区别:
管理方式:栈自动释放,堆需要GC
空间大小:栈比堆小
碎片相关:栈产生的碎片远小于堆
分配方式:栈支持静态和动态分配,而堆仅支持动态分配
效率:栈的效率比堆高
不同版本之间的intern()方法的区别 - JDK6 VS JDK6+
String s = new String("hello");
s.intern();
JDK6: 若存在常量池,则引用,若不存在,则将其添加
到常量池,并返回引用
JDK6+:若在常量池,则引用,否则在java堆中是否存在
若存在,则将堆中对象添加常量池,返回引用
,否则,在常量池创建,返回引用