1.谈谈你对JAVA的理解

  • 平台无关性(一次编译,到处运行)
  • GC(不必手动释放堆内存)
  • 语言特性(泛型、lambda)
  • 面向对象(继承,封装,多态)
  • 类库
  • 异常处理

2.平台无关性怎么实现

0
Java源码首先被编译成字节码,再由不同平台的JVM解析,Java语言在不同的平台上运行时不需要进行重新编译,Java虚拟机在执行字节码的时候,把字节码转换成具体平台上的机器指令。

补充:为什么JVM不直接将源码解析成机器码去执行?

如果直接解析成机器码去执行,那么每次执行,都需要重新做语法分析等操作。
  • 准备工作:每次执行都需要各种检查
  • 兼容性:也可以将别的语言解析成字节码

3.Java虚拟机

一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

0
JVM主要由Class Loader、Runtime Data Area、Execution Engine以及Native Interface这四个部分组成。
它主要通过Class Loader将指定格式的class文件加载到内存,并通过Execution Engine去解析class文件里的字节码并提交给操作系统去执行。 Class Loader:依据特定格式,加载class文件到内存
Execution Engine:对命令进行解析
Native Interface:融合不同开发语言的原生库为Java所用
Runtime Data Area:JVM内存空间结构模型

4.反射

JAVA反射机制是在运行状态中,
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性; 这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。

补充:

public Method[] getDeclareMethods():返回Class对象表示的类或接口的所有的成员方法对象数组,包括pubic,protected,default和private的方法对象,不包括从父类继承的方法
public Methods[] getMethods(): 返回Class对象表示的类或接口的所有公有成员方法对象数组,包括已声明的、从父类继承,实现接口的方法
反射的例子:
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)双亲委派机制

0

当类加载器收到类加载请求时,会去判断是否加载过,如果加载过,直接返回,否则,就委派给它上级,层层这样去判断,到达最底层后,还是没加载过,就去判断是否可以被加载到,可以就返回,否则,就层层往下判断能否加载到,如果最终还是加载不到就跑出异常。

作用

避免多份同样字节码加载,加载过了就不会再加载,重新加载它一定是不同的。

(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的可寻址范围
0

(2)地址空间的划分:

内核空间:是主要的操作系统程序和C运行时的空间,包含用于连接计算机硬件、调度程序、以及提供联网和虚拟内存服务的逻辑,和基于C的进程;

用户空间:java进程实际运行时使用的内存空间(32位系统用户进程最多可以访问3GB,内核代码可以访问所有物理内存;而64位系统用户进程最多可以访问512GB,内核代码也可以访问所有物理内存)

0

(3)JVM内存模型——JDK8

线程私有:程序计数器、虚拟机栈、本地方法栈
线程共享:元空间(MetaSpace)、Java堆
0

1)程序计数器(逻辑计数器,而非物理计数器)

  • 当前线程所执行的字节码行号指示器(逻辑)
  • 改变计数器的值来选取下一条需要执行的字节码指令
  • 和线程是一对一的关系即“线程私有”
  • 对Java方法计数,如果是Native方法则计数器值为Undefined
  • 不会发生内存泄露

2)java虚拟机栈(stack)

  • Java方法执行的内存模型
  • 包含多个栈帧(栈帧包含:局部变量表、操作栈、动态连接、返回地址)
0

3)局部变量表和操作数栈

  • 局部变量表:包含方法执行过程中的所有变量
  • 操作数栈:入栈、出栈、复制、交换、产生消费变量
执行add(1,2)的过程
public static int add(int a, int b){
int c = 0;
c = a + b;
return c;
}
0
递归为什么会引发java.lang.StackOverflowError异常
①原因:递归过深,栈帧数超出虚拟栈深度

  当线程执行一个方法时,就会随之创建一个栈帧,并将栈帧压入虚拟机栈,当方法执行完后,便会将栈帧出栈,因此可知,线程当前执行的方法所对应的栈帧位于栈的顶部,而我们的递归函数不断去调用自身,
每一次方法调用会涉及以下操作:

第一:每新调用一个方法,就会生成一个栈帧;

第二:它会保存当前方法栈帧的状态,将它放入虚拟机栈中;

第三:栈帧上下文切换的时候,会切换到最新的方法栈帧当中,而由于我们虚拟机栈深度是固定的,递归实现将导致栈的深度增加;如果栈帧数超过了最大深度,就会抛出java.lang.StackOverflowError异常。

②解决方法
  • 循环方法替代
  • 限制递归次数

3)本地方法栈

  • 与虚拟机栈相似,主要作用于标注了native的方法

4)元空间和永久代

①区别
元空间使用本地内存,永久代使用的是JVM内存
②MetaSpace相比PermGen的优势
jdk1.8之后,jvm移除了永久代,使用元空间。
  • 字符串常量池存在永久代中,容易出现性能问题和内存溢出
  • 类和方法的信息大小难以确定,给永久代的大小指定带来困难
  • 永久代会为GC带来不必要的复杂性
  • 方便HotSpot与其他JVM如Jrockit的集成

5)Java堆

  • 对象实例的分配区域
  • GC管理的主要区域
0
①JVM三大性能调优参数-Xms -Xmx -Xss的含义
  • -Xms:堆的初始值(该进程刚创建出来的时候,它的专属Java堆的大小。一旦对象容量超过Java堆的初始容量,Java堆将会自动扩容,最大扩容大小的-Xmx)
  • -Xmx:堆能达到的最大值(在很多情况下,-Xms和-Xmx设置成一样的。这么设置,是因为当Heap不够用时,会发生内存抖动,影响程序运行稳定性)
  • -Xss:规定了每个线程虚拟机栈(堆栈)的大小(一般256k足够,此配置会影响此进程中并发线程数的大小)
②内存分配策略
程序运行时,有三种内存分配策略:静态的、栈式的、堆式的。
  • 静态存储:编译时确定每个数据目标在运行时的存储空间需求(因而在程序编译时就可以给它们分配固定的内存空间。这种分配策略要求程序代码中不允许有可变数据结构的存在,也不允许有嵌套或者递归的结构出现,因为他们都会导致编译程序无法计算准确的存储空间)
  • 栈式存储:数据区需求在编译时未知,运行时模块入口前确定(动态的存储分配,由一个堆栈的运行栈实现的。规定在进入一个程序模块的时候,必须知道这个程序模块所需要的内存大小。按照先进后出的原则进行分配)
  • 堆式存储:编译时或运行时模块入口都无法确定,动态分配(比如可变长度串以及对象实例,堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放)
③联系:创建的数据和对象实例都保存在堆中,想要引用对象、数组时,可以在栈里定义变量保存堆中目标的首地址
0
④堆和栈的区别:
  • 管理方式:栈自动释放,堆需要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();
}
}
综上述代码分析:各部分的存储情况

元空间

Class:HelloWorld - Method:sayHello\setName\main - Field:name
Class:System 类对象、成员变量和方法

Java堆

Object:String(“forlan”) 实例
Object:HelloWorld

线程独占

Parameter reference:“forlan” to String object 地址引用
Variable reference:“hw” to HelloWorld object 地址引用
Local Variables:a with 1 值,lineNo 行号

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
}
Jdk6
s创建的时候,首先在常量池中创建“a”,然后在heap中创建了string对象。当使用intern时,企图将string对象放到常量池中,但是常量池已经有a了,所以放置失败。定义s2时,直接使用了常量池中的a,所以两个地址不一样。 创建s3时,在heap中创建string对象,使用intern函数,将对象的副本放置到常量池中,s4引用常量池中的aa,但是由于是string对象的副本,所以两个的地址不同
Jdk6+
s创建的时候,首先在常量池中创建“a”,然后在heap中创建了string对象。当使用intern时,企图将string对象放到常量池中,但是常量池已经有a了,所以放置失败。定义s2时,直接使用了常量池中的a,所以两个地址不一样。
创建s3时,在heap中创建string对象,使用intern函数,将对象的引用放置到常量池中,s4引用常量池中的aa,由于是string对象的引用,所以两个的地址相同

JVM你了解?的更多相关文章

  1. 46张PPT讲述JVM体系结构、GC算法和调优

    本PPT从JVM体系结构概述.GC算法.Hotspot内存管理.Hotspot垃圾回收器.调优和监控工具六大方面进行讲述.(内嵌iframe,建议使用电脑浏览) 好东西当然要分享,PPT已上传可供下载 ...

  2. java 利用ManagementFactory获取jvm,os的一些信息--转

    原文地址:http://blog.csdn.net/dream_broken/article/details/49759043 想了解下某个Java项目的运行时jvm的情况,可以使用一些监控工具,比如 ...

  3. Jvm 内存浅析 及 GC个人学习总结

    从诞生至今,20多年过去,Java至今仍是使用最为广泛的语言.这仰赖于Java提供的各种技术和特性,让开发人员能优雅的编写高效的程序.今天我们就来说说Java的一项基本但非常重要的技术内存管理 了解C ...

  4. JVM类加载

    JVM的类加载机制就是:JVM把描述类的class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被JVM直接使用的Java类型 ClassLoader JVM中的ClassLoade ...

  5. Java虚拟机 JVM

    finalize();(不建议使用,代价高,不确定性大) 如果你在一个类中覆写了finalize()方法, 那么你可以在第一次被GC的时候,挽救一个你想挽救的对象,让其不被回收,但只能挽救一次. GC ...

  6. 在 Linux 中安装 Oracle JDK 8 以及 JVM 的类加载机制

    参考资料 该文中的内容来源于 Oracle 的官方文档 Java SE Tools Reference .Oracle 在 Java 方面的文档是非常完善的.对 Java 8 感兴趣的朋友,可以直接找 ...

  7. MapReduce剖析笔记之六:TaskTracker初始化任务并启动JVM过程

    在上面一节我们分析了JobTracker调用JobQueueTaskScheduler进行任务分配,JobQueueTaskScheduler又调用JobInProgress按照一定顺序查找任务的流程 ...

  8. java太low,又舍不得jvm平台的丰富资源?试试kotlin吧(一)

    尝试kotlin的起因 因为各种原因(版权,人员招聘),公司的技术体系从c#转到了java,我花了大概两周的时间来上手java,发现java的语法还是非常简单的,基本看着代码就知道什么意思.学习jav ...

  9. Jvm --- 常用工具

    jps:虚拟机进程状况工具 JVM Process Status Tool. 可以列出所有目前正在运行虚拟机的进程. jps -l 详细参数: -q 输出LVMID,省略主类名称 -m 输出虚拟机进程 ...

  10. JVM虚拟机结构

    JVM的主要结构如下图所示,图片引用自舒の随想日记. 方法区和堆由所有线程共享,其他区域都是线程私有的 程序计数器(Program Counter Register) 类似于PC寄存器,是一块较小的内 ...

随机推荐

  1. 【win10】win10下两个显示器不同桌面壁纸

    win10系统下,双屏显示为不同的桌面壁纸 操作: 1.鼠标右键点击个性化 2.点击背景选项 3.在图片上右键选择要添加为背景的图片 同理,将另一个屏幕壁纸设为监视器1 最后效果为两个分屏为不同桌面壁 ...

  2. 【Linux】postfix大坑笔记

    由于需要,想弄一个自动发送邮件的mailx或者sendmail 但是执行 echo "test" | mail -s "Worning mail !" xxxx ...

  3. 攻防世界 - Web(二)

    supersqli: (!!!) 1.判断有误注入,1'报错, 1' 报错,1'# 正常且为True,1' and 1=1# 正常且为True,1' and 1=2# 正常且为False,所以它里边存 ...

  4. 【葵花宝典】kolla部署OpenStack-AllinOne

    1.关闭防火墙以及内核安全机制 systemctl stop firewalld systemctl disable firewalld ##永久性关闭 setenforce 0 sed -i 's/ ...

  5. uni-app开发经验分享十三:实现手机扫描二维码并跳转全过程

    最近使用 uni-app 开发 app ,需要实现一个调起手机摄像头扫描二维码功能,官网API文档给出了这样一个demo: // 允许从相机和相册扫码 uni.scanCode({ success: ...

  6. 并发编程常用工具类(二) SymaPhore实现线程池

    1.symaPhore简介 symaphore(信号量)用来控制同时访问某个资源的线程数量,一般用在并发流量控制.个人对它的理解相当于是接待室每次只能接待固定数量的人,当达到最高接待数的时候,其他人就 ...

  7. 【UltraISO】中文破解版

    下载链接:https://cn.ultraiso.net/uiso9_cn.exe简体中文版专用:   注册名:Guanjiu    注册码:A06C-83A7-701D-6CFC多国语言版专用:   ...

  8. Python+Selenium+Unittest实现PO模式web自动化框架(8)

    1.main.py模块的功能 最后就是要有一个项目入口,并且是需要加载测试用例集. # --^_^-- coding:utf-8 --^_^-- # @Remark:运行入口 "" ...

  9. 使用Python对MySQL数据库插入二十万条数据

    1.当我们测试的时候需要大量的数据的时候,往往需要我们自己造数据,一条一条的加是不现实的,这时候就需要使用脚本来批量生成数据了. import pymysql import random import ...

  10. 扩展PE头属性说明

    CRC检测的算法就是checksum 以下是DllCharacteristics的参数说明