JAVA基础笔试面试总结(JVM与GC)

一、 JVM

1. JVM原理
    1 > JVM是 Java程序运行的环境,也是一个操作系统的一个应用程序,一个进程,也有自己的生命周期,也有自己的代码和数据空间。
    2 > JVM 在整个JDK中处于底层,负责与操作系统的交互,用来屏蔽操作系统环境,提供一个完整的JAVA运行环境。
    3 > 操作系统装入JVM是通过JDK中java.exe来完成的,其中,四步实现JVM环境搭建:

1 - 创建JVM装载环境和配置

      -  首先查找jre路径(Java是通过GetApplicationHome api来获得当前的Java.exe绝对路径,进一步确定jre路径)

       -  然后装载JVM.cfg文件(GetArch函数可以判断出:JRE路径+\lib+\ARCH(CPU构架)+\JVM.cfgARCH(CPU构架))

       -  在运行java XXX时,一般地,CheckJVMType函数会从 根据JVM.cfg定义的JVM 获取JVM的类型。

       -  最后获得JVM.dll的路径:JRE路径+\bin+\JVM类型字符串+\JVM.dll就是JVM的文件路径

2 - 装载JVM.dll

       -  通过上述的步骤找到了JVM路径,Java通过LoadJavaVM来装入JVM.dll文件(装入工作很简单就是调用Windows API函数:LoadLibrary装载JVM.dll动态连接库.然后把JVM.dll中的导出函数JNI_CreateJavaVM和JNI_GetDefaultJavaVMInitArgs挂接到InvocationFunctions变量的CreateJavaVM和GetDefaultJavaVMInitArgs函数指针变量上。JVM.dll的装载工作宣告完成。)

3 - 初始化JVM.dll并挂在到JNIEnv(JNI调用接口)实例

        -  初始化JVM,获得本地调用接口,这样就可以在Java中调用JVM的函数了.调用InvocationFunctions->CreateJavaVM也就是JVM中JNI_CreateJavaVM方法获得JNIEnv结构的实例

4 - 调用JNIEnv实例装载并处理class类

        - java 将.java文件编译成字节码文件,由类加载器加载字节码文件,实质上是把类文件从硬盘读取到内存中(详细:JVM加载class文件的原理机制- http://www.cnblogs.com/blogonfly/articles/3778426.html)

2.JVM加载class文件的原理机制(http://www.cnblogs.com/blogonfly/articles/3778426.html

①.java中的所以类,必须被加载到JVM中才能运行,这个加载是由类加载器完成的,类加载器所做的工作实质上是把类文件从硬盘读取到内存中

②.java中的类大致分为三种:

1、系统类

2、扩展类

3、程序员自定义的类

③.类加载的方式,两种

1、隐式装载,程序在运行过程中碰到new等方式生成对象时,隐式调用类加载器加载对应的类到JVM中

2、显示装载,通过class.forName()等方法,显示加载需要的类

④.类加载的动态性体现

    一个应用程序总是由n多个类组成,java程序启动时,并不是一次性把全部的类加载后在运行的,它总是先把保证运行的基础类一次性加载到JVM中,其他的类等到JVM用到的时候再加载,这样节约内存开销。

⑤.java类装载器

java中的类装载器把类载入JVM中,值得注意的是JVM的类装载器有3个

Bootstrap ClassLoader - 负责加载系统类

ExtClassLoader - 负责加载扩展类

AppClassLoader - 负责加载应用类

三个类加载器一方面各自负责各自的区块,另一方方面实现委托模型

⑥.类加载器之间是如何协调工作的

    碰到一个类需要加载时,java采用了委托模型机制,这个机制简单来讲,就是类装载器有载入类的需求时,会先请示器parent使用其搜索路径帮忙载入,如果parent找不到,那么才会自己依照自己的搜索路径搜索类。

下面举一个例子来说明,为了更好的理解,先弄清楚几行代码:

public class Test{

    public static void main(String[] arg){

      ClassLoader c  = Test.class.getClassLoader();  //获取Test类的类加载器

        System.out.println(c); 

      ClassLoader c1 = c.getParent();  //获取c这个类加载器的父类加载器

        System.out.println(c1);

      ClassLoader c2 = c1.getParent();//获取c1这个类加载器的父类加载器

        System.out.println(c2);

  }

}

运行结果:

sun.misc.Launcher$AppClassLoader@addbf1

sun.misc.Launcher$ExtClassLoader@42e816

null

可以看出Test是由AppClassLoader加载器加载的

AppClassLoader的Parent 加载器是 ExtClassLoader 但是ExtClassLoader的Parent为 null 是怎么回事呵,如果留意的话,前面有提到Bootstrap Loader是用C++语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体,所以在java程序代码里试图打印出其内容时,我们就会看到输出为null。

类加载器ClassLoader(抽象类)描述JVM加载class文件的原理机制

类装载器就是寻找类或接口字节码文件进行解析并构造JVM内部对象表示的组件,

⑦.装载类到JVM的步骤

1、装载:查找和导入class文件

2、连接:其中解析步骤是可选的

1)检查:检查载入的class文件数据的正确性

2)准备:给类的静态变量分配存储空间

3)解析:将符号引用转换成直接引用

3、初始化:对静态变量,静态代码块执行初始化

    java装载类使用"全盘负责委托机制":是指一个ClassLoader装载一个类时,除非显示使用另一个ClassLoader,该类所依赖及引用的类也由这个ClassLoader载入。"委托机制"是指先委托父类装载器寻找目标类,只有在找不到的情况下才从自己的路径中查找并载入。这一点是从安全的方面考虑的,试想一下如果有人写了一个恶意的基础类(如java.lang.String)并加载到JVM将引起严重后果,但是全盘负责制,java.lang.String永远是由根装载器来装载的,避免以上情况发生。

⑧.Java 类加载器怎么实现将同一个对象加载两次?

平时了解到的就是Java类加载器因为双亲委托模型,一个类只会被加载一次。

 - 创建一个classloader,parent设置成null (参见: http://sunfish.iteye.com/blog/1472643)

 - 创建两个自定义类加载器同时加载 类的包路径和类加载器决定类的唯一性

3. JVM内存模型

根据Java虚拟机规范,JVM将内存划分为:
New(年轻代)
Tenured(年老代)
永久代(Perm)
  其中New和Tenured属于堆内存,Heap = {Old + NEW = { Eden , from, to } },堆内存会从JVM启动参数(-Xmx:3G)指定的内存中分配,Perm不属于堆内存,有虚拟机直接分配,但可以通过-XX:PermSize -XX:MaxPermSize 等参数调整其大小。
 

年轻代(New):年轻代用来存放JVM刚分配的Java对象

New又分为几个部分:

Eden:Eden用来存放JVM刚分配的对象

Survivor1

Survivro2:两个Survivor空间一样大,当Eden中的对象经过垃圾回收没有被回收掉时,会在两个Survivor之间来回Copy,当满足某个条件,比如Copy次数,就会被Copy到Tenured。显然,Survivor只是增加了对象在年轻代中的逗留时间,增加了被垃圾回收的可能性。

年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代

永久代(Perm):永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间。

4.JVM调优建议

    首先,Jdk自带的监控工具JvisualVM使用:http://jingyan.baidu.com/article/e9fb46e172e3747521f76611.html
    ms/mx:定义YOUNG+OLD段的总尺寸,ms为JVM启动时YOUNG+OLD的内存大小;mx为最大可占用的YOUNG+OLD内存大小。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销。
    NewSize/MaxNewSize:定义YOUNG段的尺寸,NewSize为JVM启动时YOUNG的内存大小;MaxNewSize为最大可占用的YOUNG内存大小。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销。
    PermSize/MaxPermSize:定义Perm段的尺寸,PermSize为JVM启动时Perm的内存大小;MaxPermSize为最大可占用的Perm内存大小。在用户生产环境上一般将这两个值设为相同,以减少运行期间系统在内存申请上所花的开销。
    SurvivorRatio:设置Survivor空间和Eden空间的比例

 

猜你喜欢

转载自blog.csdn.net/Juwenzhe_HEBUT/article/details/77915908