JVM你了解?
1.谈谈你对JAVA的理解
- 平台无关性(一次编译,到处运行)
- GC(不必手动释放堆内存)
- 语言特性(泛型、lambda)
- 面向对象(继承,封装,多态)
- 类库
- 异常处理
2.平台无关性怎么实现
补充:为什么JVM不直接将源码解析成机器码去执行?
- 准备工作:每次执行都需要各种检查
- 兼容性:也可以将别的语言解析成字节码
3.Java虚拟机
一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
4.反射
补充:
package com.interview.javabasic.reflect; public class Robot {
private String name;
public void sayHi(String helloSentence){
System.out.println(helloSentence + " " + name);
}
private String throwHello(String tag){
return "Hello " + tag;
}
}
package com.interview.javabasic.reflect; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; public class ReflectSample {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {
Class rc = Class.forName("com.interview.javabasic.reflect.Robot"); Robot r = (Robot) rc.newInstance();
System.out.println("Class name is " + rc.getName()); Method getHello = rc.getDeclaredMethod("throwHello", String.class);
getHello.setAccessible(true); // 调用私有方法需要加,不然会报错Exception in thread "main" java.lang.IllegalAccessException
Object str = getHello.invoke(r, "Bob");
System.out.println("getHello result is " + str); Method sayHi = rc.getMethod("sayHi", String.class);
sayHi.invoke(r, "Welcome"); Field name = rc.getDeclaredField("name");
name.setAccessible(true);
name.set(r, "Alice");
sayHi.invoke(r, "Welcome");
}
}
5.谈谈classloader
(1)类从编译到执行的过程
- 编译器将Forlan.java 源文件编译为Forlan.class 字节码文件
- ClassLoader将字节码转换为JVM中的 Class<Forlan>对象
- JVM利用Class<Forlan>对象实例化为Forlan对象
ClassLoader在java中有着非常重要的作用,Java的核心组件中所有class都是由ClassLoader进行加载的.它主要工作在class装载的加载阶段,其主要作用是从外部系统获得class二进制数据流,通过将class文件里的二进制数据流装进系统,然后交给java虚拟机进行连接,初始化等操作。
种类
- BootStrapClassLoader:C++ 编写,加载核心库java.*
- ExtClassLoader:Java编写,加载扩展库javax.*(不是一次性加载,用到才加载)
- AppClassLoader:Java编写,加载程序所在目录(class.path)
- 自定义ClassLoader:Java编写定制加载
(2)自定义ClassLoader的实现
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
protected final Class<?> defineClass(byte[] b, int off, int len) throws ClassFormatError{
return defineClass(null, b, off, len, null);
}
具体实现
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){
byte[] b = loadClassData(name); return defineClass(name,b,0,b.length);
} /** 用于加载类文件 */
private byte[] loadClassData(String name) {
/** 全路径1 */
name = path + name + ".class";
/** jdk1.8新特性,不用手动关闭 */
try(
InputStream in = new FileInputStream(new File(name));
ByteArrayOutputStream out = new ByteArrayOutputStream();)
{ int i = 0;
while ((i = in.read()) != -1){ out.write(i);
} return out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
} }
}
//检测类
public class ClassLoaderChecker{
public static void main(String[] args){
MyClassLoader m=new MyClassLoader(path:"Users/baidu/Desktop/",classLoaderName:"myClassLoader");
Class c=m.loadClass(name:"Wali");
System.out.println(c.getClassLoader());//结果是MyClassLoader
System.out.println(c.getClassLoader().getParent());//结果是AppClassLoader
System.out.println(c.getClassLoader());//结果是ExtClassLoader
System.out.println(c.getClassLoader());//结果是null
c.newInstance();
}
(3)双亲委派机制
当类加载器收到类加载请求时,会去判断是否加载过,如果加载过,直接返回,否则,就委派给它上级,层层这样去判断,到达最底层后,还是没加载过,就去判断是否可以被加载到,可以就返回,否则,就层层往下判断能否加载到,如果最终还是加载不到就跑出异常。
作用
避免多份同样字节码加载,加载过了就不会再加载,重新加载它一定是不同的。
(4)类的加载方式
隐式加载:new
显示加载:Classloder.loadClass,Class.forName等获取Class对象,再通过newInstance方法获取对象
(5)类的装载过程
主要有三个步骤:装载(Load),链接(Link)和初始化(Initialize)。
加载:通过ClassLoader加载class文件字节码,生成Class对象
链接:
校验:检查加载的class的正确性和安全性
准备:为类变量分配存储空间并设置类变量初始值
解析:JVM将常量池内的符号引用转换为直接引用
初始化:执行类变量赋值和静态代码块
(6)loadClass和forname区别
forname得到的class已经完成初始化
loadclass只是加载,并没有链接和初始化
- JDBC中加载数据库驱动用到Class.forName(“com.mysql.jdbc.Driver”),Driver中有静态代码块,所以需要用到forName()
- Spring IOC资源加载器获取资源(即读取配置文件时),用到class.getClassLoader(),是为了加快初始化速度,延迟加载
(7)类什么时候才被初始化
- new对象
- 访问某个类或接口的静态变量,或者对该静态变量赋值
- 调用类的静态方法
- 反射——Class.forName方法
- 初始化子类,会首先初始化父类)
- JVM启动时标明的启动类,即文件名和类名相同的那个类
6.Java内存模型
(1)内存相关(了解)
简介:
计算机所有程序都是在内存中运行的,只不过这个内存可能包括虚拟内存,同时也离不开硬盘这样的外存知识;在程序执行的过程中,需要不断地将内存的逻辑地址和物理地址映射起来,找到相关的指令以及数据去执行;作为操作系统进程,java运行时面临着和其他进程相同的内存限制,即受限于操作系统架构提供的可寻址空间。
可寻址空间由处理器的位数决定:
- 32位处理器:2^32的可寻址范围(4GB)
- 64位处理器:2^64的可寻址范围
(2)地址空间的划分:
内核空间:是主要的操作系统程序和C运行时的空间,包含用于连接计算机硬件、调度程序、以及提供联网和虚拟内存服务的逻辑,和基于C的进程;
用户空间:java进程实际运行时使用的内存空间(32位系统用户进程最多可以访问3GB,内核代码可以访问所有物理内存;而64位系统用户进程最多可以访问512GB,内核代码也可以访问所有物理内存)
(3)JVM内存模型——JDK8
1)程序计数器(逻辑计数器,而非物理计数器)
- 当前线程所执行的字节码行号指示器(逻辑)
- 改变计数器的值来选取下一条需要执行的字节码指令
- 和线程是一对一的关系即“线程私有”
- 对Java方法计数,如果是Native方法则计数器值为Undefined
- 不会发生内存泄露
2)java虚拟机栈(stack)
- Java方法执行的内存模型
- 包含多个栈帧(栈帧包含:局部变量表、操作栈、动态连接、返回地址)
3)局部变量表和操作数栈
- 局部变量表:包含方法执行过程中的所有变量
- 操作数栈:入栈、出栈、复制、交换、产生消费变量
执行add(1,2)的过程
public static int add(int a, int b){
int c = 0;
c = a + b;
return c;
}
递归为什么会引发java.lang.StackOverflowError异常
①原因:递归过深,栈帧数超出虚拟栈深度
当线程执行一个方法时,就会随之创建一个栈帧,并将栈帧压入虚拟机栈,当方法执行完后,便会将栈帧出栈,因此可知,线程当前执行的方法所对应的栈帧位于栈的顶部,而我们的递归函数不断去调用自身,
每一次方法调用会涉及以下操作:
第一:每新调用一个方法,就会生成一个栈帧;
第二:它会保存当前方法栈帧的状态,将它放入虚拟机栈中;
第三:栈帧上下文切换的时候,会切换到最新的方法栈帧当中,而由于我们虚拟机栈深度是固定的,递归实现将导致栈的深度增加;如果栈帧数超过了最大深度,就会抛出java.lang.StackOverflowError异常。
②解决方法
- 循环方法替代
- 限制递归次数
3)本地方法栈
- 与虚拟机栈相似,主要作用于标注了native的方法
4)元空间和永久代
①区别
②MetaSpace相比PermGen的优势
- 字符串常量池存在永久代中,容易出现性能问题和内存溢出
- 类和方法的信息大小难以确定,给永久代的大小指定带来困难
- 永久代会为GC带来不必要的复杂性
- 方便HotSpot与其他JVM如Jrockit的集成
5)Java堆
- 对象实例的分配区域
- GC管理的主要区域
①JVM三大性能调优参数-Xms -Xmx -Xss的含义
- -Xms:堆的初始值(该进程刚创建出来的时候,它的专属Java堆的大小。一旦对象容量超过Java堆的初始容量,Java堆将会自动扩容,最大扩容大小的-Xmx)
- -Xmx:堆能达到的最大值(在很多情况下,-Xms和-Xmx设置成一样的。这么设置,是因为当Heap不够用时,会发生内存抖动,影响程序运行稳定性)
- -Xss:规定了每个线程虚拟机栈(堆栈)的大小(一般256k足够,此配置会影响此进程中并发线程数的大小)
②内存分配策略
- 静态存储:编译时确定每个数据目标在运行时的存储空间需求(因而在程序编译时就可以给它们分配固定的内存空间。这种分配策略要求程序代码中不允许有可变数据结构的存在,也不允许有嵌套或者递归的结构出现,因为他们都会导致编译程序无法计算准确的存储空间)
- 栈式存储:数据区需求在编译时未知,运行时模块入口前确定(动态的存储分配,由一个堆栈的运行栈实现的。规定在进入一个程序模块的时候,必须知道这个程序模块所需要的内存大小。按照先进后出的原则进行分配)
- 堆式存储:编译时或运行时模块入口都无法确定,动态分配(比如可变长度串以及对象实例,堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放)
③联系:创建的数据和对象实例都保存在堆中,想要引用对象、数组时,可以在栈里定义变量保存堆中目标的首地址
④堆和栈的区别:
- 管理方式:栈自动释放,堆需要GC(JVM可以针对内存栈进行管理操作,而且该内存的释放是编译器就可以操作的内容)
- 空间大小:栈比堆小(由本身存储的数据特性决定)
- 碎片相关:栈产生的碎片远小于堆(对于堆空间而言,即使垃圾回收器可以进行自动堆内存回收,但是堆空间的活动量相对于栈空间而言比较大,很有可能存在长时间的堆空间分配和释放操作。而且垃圾回收器不是实时的,它有可能使得堆空间的内存碎片,逐渐累积起来。针对栈空间而言,因为本身就是一个堆栈的数据结构,操作都是一一对应的,而且每一个最小单位的结构栈帧和堆空间中复杂的内存结构不一样,所以在使用过程很少出现内存碎片)
- 分配方式:栈支持静态和动态分配,而堆仅支持动态分配
- 效率:栈的效率比堆高(因为内存块本身的排列就是一个典型的堆栈结构,所以栈空间的效率自然比起堆空间要高很多,而且计算机底层内存空间本身就使用了最基础的堆栈结构使得栈空间和底层结构更加符合,它的操作也变得简单,就是最简单的两个指令:入栈和出栈;栈空间相对堆空间而言的弱点是灵活程度不够,特别是在动态管理的时候。而堆空间最大的优势在于动态分配,因为它在计算机底层实现可能是一个双向链表结构,所以它在管理的时候操作比栈空间复杂很多,自然它的灵活度就高了,但是这样的设计也使得堆空间的效率不如栈空间,而且低很多)
7.元空间、堆、栈独占部份间的联系——内存角度
public class HelloWorld{
private String name;
public void sayHello(){
System.out.println("Hello "+name);
}
public void setName(String name){
this.name = name;
}
public static void main(String[] args){
int a = 1;
HelloWorld hw = new HelloWorld();
hw.setName("forlan");
hw.sayHello();
}
}
元空间
Java堆
线程独占
8.不同JDK版本之间的intern()方法的区别——JDK6 VS JDK6+
String s = new Stirng("a"); s.intern();
JDK6:当调用 intern 方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,将此字符串对象添加到字符串常量池中,并且返回该字符串的引用。
JDK6+:当调用 intern 方法时,如果字符串常量池先前已创建出该字符串对象,则返回池中的该字符串的引用。否则,如果该字符串对象已经存在于Java堆中,则将堆中此对象的引用添加到字符串常量池中,并且返回该引用;如果堆中不存在,则在池中创建该字符串并返回其引用。
注:在JDK1.6的时候,字符串常量池是存放在Perm Space中的(Perm Space和堆是相隔而开的),在1.6+的时候,移到了堆内存中。
public static void main(String[] args) {
String s1 = new String("a");
s1.intern();
String s2 = "a";
System.out.println(s1 == s2); //Jdk6:false Jdk6+:false String s3 = new String("a") + new String("a");
s3.intern(); // 不加都是false
String s4 = "aa";
System.out.println(s3 == s4); //Jdk6:false Jdk6+:true
}
JVM你了解?的更多相关文章
- 46张PPT讲述JVM体系结构、GC算法和调优
本PPT从JVM体系结构概述.GC算法.Hotspot内存管理.Hotspot垃圾回收器.调优和监控工具六大方面进行讲述.(内嵌iframe,建议使用电脑浏览) 好东西当然要分享,PPT已上传可供下载 ...
- java 利用ManagementFactory获取jvm,os的一些信息--转
原文地址:http://blog.csdn.net/dream_broken/article/details/49759043 想了解下某个Java项目的运行时jvm的情况,可以使用一些监控工具,比如 ...
- Jvm 内存浅析 及 GC个人学习总结
从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...
- JVM类加载
JVM的类加载机制就是:JVM把描述类的class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被JVM直接使用的Java类型 ClassLoader JVM中的ClassLoade ...
- Java虚拟机 JVM
finalize();(不建议使用,代价高,不确定性大) 如果你在一个类中覆写了finalize()方法, 那么你可以在第一次被GC的时候,挽救一个你想挽救的对象,让其不被回收,但只能挽救一次. GC ...
- 在 Linux 中安装 Oracle JDK 8 以及 JVM 的类加载机制
参考资料 该文中的内容来源于 Oracle 的官方文档 Java SE Tools Reference .Oracle 在 Java 方面的文档是非常完善的.对 Java 8 感兴趣的朋友,可以直接找 ...
- MapReduce剖析笔记之六:TaskTracker初始化任务并启动JVM过程
在上面一节我们分析了JobTracker调用JobQueueTaskScheduler进行任务分配,JobQueueTaskScheduler又调用JobInProgress按照一定顺序查找任务的流程 ...
- java太low,又舍不得jvm平台的丰富资源?试试kotlin吧(一)
尝试kotlin的起因 因为各种原因(版权,人员招聘),公司的技术体系从c#转到了java,我花了大概两周的时间来上手java,发现java的语法还是非常简单的,基本看着代码就知道什么意思.学习jav ...
- Jvm --- 常用工具
jps:虚拟机进程状况工具 JVM Process Status Tool. 可以列出所有目前正在运行虚拟机的进程. jps -l 详细参数: -q 输出LVMID,省略主类名称 -m 输出虚拟机进程 ...
- JVM虚拟机结构
JVM的主要结构如下图所示,图片引用自舒の随想日记. 方法区和堆由所有线程共享,其他区域都是线程私有的 程序计数器(Program Counter Register) 类似于PC寄存器,是一块较小的内 ...
随机推荐
- docker 安装linux centos 环境
如何在centos7中使用docker创建一个支持ssh连接的容器 以下内容已有现成的,这里借用下这哥们的帖子https://www.cnblogs.com/caidingyu/p/10642158. ...
- Git软件安装过程
Git程序安装过程 官网: https://git-scm.com/ 下载: https://git-scm.com/downloads 我的操作系统是 Windows + 64位的 https:// ...
- mysql中的kill
show processlist;查看id, 然后 kill id ; 就行了.
- 【EXP】WINDOWS下如何导出
有些时候需要在windows下通过远程来导出数据 那么windows下怎么导出呢 例子: exp hr/hr@192.168.1.222:1521/zhang file=d:backup.dmp lo ...
- HarmonyOS三方件开发指南(5)——Photoview组件
PhotoView使用说明 1. PhotoView功能介绍1.1 组件介绍: PhotoView是一个继承自Image的组件,不同之处在于:它可以进行图击放大功能,手势缩放功能(暂无 ...
- KeepAlive安装以及简单配置
操作系统:Centos7.3 一.依赖安装 首先安装相关依赖: yum install -y gcc openssl-devel popt-devel yum -y install libnl lib ...
- 容器编排系统K8s之包管理器helm基础使用(二)
前文我们介绍了helm的相关术语和使用helm安装和卸载应用,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14305902.html:今天我们来介绍下自定义 ...
- uni-app开发经验分享四: 实现文字复制到选择器中
这里分享一个我经常用到的一个方法,主要是用来复制文字内容,具体代码如下: var that=this; if(!document){ uni.setClipboardData({ data:复制的值, ...
- 超微服务器重置ipmi登录密码
超微服务器的ipmi登录密码不对,需要重置但是bios内并没有找到可以设置的选项. 以下是解决办法: 安装IPMITOOLyum install ipmitool -y 执行以下命令加载模块:modp ...
- 深入理解SPI机制-服务发现机制
https://www.jianshu.com/p/3a3edbcd8f24 SPI ,全称为 Service Provider Interface,是一种服务发现机制.它通过在ClassPath路径 ...