查看“【面试:JVM】”的源代码
←
【面试:JVM】
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
[[category:面试]] == 说说Java虚拟机的生命周期及体系结构?<ref>[[Java虚拟机生命周期和体系结构]]</ref> == == Java 是如何实现跨平台的? == 注意:'''跨平台的是 Java 程序,而不是 JVM'''。 JVM 是用 C/C++ 开发的,是编译后的机器码,不能跨平台,不同平台下需要安装不同版本的 JVM。 所谓“跨平台”,即实现“一次编译,到处运行”: # 我们编写的 Java 源码,编译后会生成一种 .class 文件,称为字节码文件; # Java 虚拟机(JVM)负责将字节码文件翻译成特定平台下的机器码然后运行。 也就是说,只要在不同平台上安装对应的 JVM,就可以运行字节码文件,运行我们编写的 Java 程序,这个过程,我们编写的 Java 程序没有做任何改变,仅仅是通过 JVM 这一“中间层”,就能在不同平台上运行(不同平台使用不同的 JVM 实现),从而实现“跨平台”。 == 什么是 JVM ? == JVM,即 Java 虚拟机(“Java Virtual Machine”)。它通过模拟一个计算机来达到一个计算机所具有的的计算功能。JVM 能够跨计算机体系结构来执行 Java 字节码,主要是由于 JVM 屏蔽了与各个计算机平台相关的软件或者硬件之间的差异,使得与平台相关的耦合统一由 JVM 提供者来实现。 === JVM 由哪些部分组成? === JVM 的结构基本上由 4 部分组成: # '''类加载器''',在 JVM 启动时或者类运行时将需要的 class 加载到 JVM 中。 # '''执行引擎''',执行引擎的任务是负责执行 class 文件中包含的字节码指令,相当于实际机器上的 CPU。 # '''内存区''',将内存划分成若干个区以模拟实际机器上的存储、记录和调度功能模块,如实际机器上的各种功能的寄存器或者 PC 指针的记录器等。 # '''本地方法调用''',调用 C 或 C++ 实现的本地方法的代码返回结果。 == 说一说类加载器?<ref>[[深入理解JVM:虚拟机类加载机制]]</ref> == Java虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这个过程被称作虚拟机的类加载机制。 === 类的生命周期 === 类的生命周期:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading); [[File:类的生命周期.jpg|600px]] === 类加载的过程 === 类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。 一般来说,Java 虚拟机使用 Java 类的方式如下: 1、Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。 2、类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance() 方法就可以创建出该类的一个对象。 实际的情况可能更加复杂,比如 Java 字节代码可能是通过工具动态生成的,也可能是通过网络下载的。 类加载的过程: # 加载 # 验证 # 文件格式验证 # 元数据验证 # 字节码验证 # 符号引用验证 # 准备 # 解析 # 初始化 === 类加载器 === Java虚拟机设计团队有意把类加载阶段中的“通过一个类的全限定名来获取描述该类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类。实现这个动作的代码被称为“类加载器”(Class Loader)。 对于 JVM 而言,只存在两种不同的类加载器: # “'''启动类加载器'''”(BootstrapClassLoader):这个类加载器使用 C++ 语言实现(对于Hotspot而言),是'''虚拟机自身的一部分'''; # '''其他所有的类加载器''':这些类加载器都由 Java 语言实现,独立存在于虚拟机外部,并且全都'''继承自抽象类“java.lang.ClassLoader”'''。 对于 Java 开发者来说,可以进一步划分为“三层类加载器”: :[[File:类加载器双亲委派模型.jpg|400px]] # '''启动类加载器(Bootstrap Class Loader)''':负责加载存放在“'''<JAVA_HOME>\lib'''”目录,或者被“'''-Xbootclasspath'''”参数所指定的路径中存放的,而且是Java虚拟机能够识别的类库加载到虚拟机的内存中。 #* JVM 按照文件名识别类库(如rt.jar、tools.jar),名字不符合的类库即使放在lib目录中也不会被加载; #* 启动类加载器无法被Java程序直接引用,用户在编写自定义类加载器时,如果需要把加载请求委派给引导类加载器去处理,那直接使用'''null'''代替即可。 # '''扩展类加载器(Extension Class Loader)''':它负责加载“'''<JAVA_HOME>\lib\ext'''”目录中,或者被“'''java.ext.dirs'''”系统变量所指定的路径中所有的类库。 #* 这个类加载器是在类“sun.misc.Launcher$'''ExtClassLoader'''”中以Java代码的形式实现的。 #* 由于扩展类加载器是由 Java 代码实现的,开发者可以直接在程序中使用扩展类加载器来加载 Class 文件。 # '''应用程序类加载器(Application Class Loader)''':它负责加载用户类路径('''ClassPath''')上所有的类库。 #: 由于应用程序类加载器是“ClassLoader”类中的“'''getSystemClassLoader()'''”方法的返回值,所以有些场合中也称它为“'''系统类加载器'''”。 #* 这个类加载器由“sun.misc.Launcher$'''AppClassLoader'''”来实现。 #* 如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。开发者同样可以直接在代码中使用这个类加载器。 === 双亲委派模型 === 双亲委派模型的工作过程: 如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是'''把这个请求委派给父类加载器去完成''',每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,'''只有当父加载器反馈自己无法完成这个加载请求'''(它的搜索范围中没有找到所需的类)'''时,子加载器才会尝试自己去完成加载'''。 * 要求除了顶层的启动类加载器外,其余的类加载器都应有自己的父类加载器。 * 类加载器之间的父子关系,通常使用组合(Composition)而非继承(Inheritance)的关系来复用父加载器的代码。 * 优点:Java 中的类随着它的类加载器一起具备了一种带有优先级的层次关系。 例如: : 类 java.lang.Object,它存放在 rt.jar 之中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此 Object 类在程序的各种类加载器环境中都能够保证是同一个类。 : 反之,如果没有使用双亲委派模型,都由各个类加载器自行去加载的话,如果用户自己也编写了一个名为 java.lang.Object 的类,并放在程序的 ClassPath 中,那系统中就会出现多个不同的 Object 类,Java 类型体系中最基础的行为也就无从保证,应用程序将会变得一片混乱。 : 如果写一个与 rt.jar 类库中已有类重名的 Java 类,将会发现它可以正常编译,但永远无法被加载运行。 : 【如果使用自定义类加载器的 defineClass() 方法去加载一个以“java.lang”开头的类,会收到一个由 Java 虚拟机内部抛出的“java.lang.SecurityException:Prohibited package name:java.lang”异常。】 === Java 虚拟机是如何判定两个 Java 类是相同的? === 对于任意一个类,都必须由加载它的类加载器和这个类本身一起共同确立其在 Java 虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称空间。 即:比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义; 所谓“相等”,包括: # Class对象的“equals()”、“isAssignableFrom()”、“isInstance()”方法的返回结果; # “instanceof”关键字做对象所属关系判定; Java 虚拟机不仅要看'''类的全名'''是否相同,还要看'''加载此类的类加载器'''是否一样。 只有两者都相同的情况,才认为两个类是相同的。 '''即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的'''。 : 比如一个 Java 类 com.example.Sample,编译之后生成了字节代码文件 Sample.class。两个不同的类加载器 ClassLoaderA 和 ClassLoaderB 分别读取了这个 Sample.class文件,并定义出两个 java.lang.Class 类的实例来表示这个类: :* 这两个实例是不相同的; :* 对于 Java 虚拟机来说,它们是不同的类。试图对这两个类的对象进行相互赋值,会抛出运行时异常 ClassCastException。 == 类似 -Xms、-Xmn 这些参数的含义? == 堆内存分配: # -Xms:初始分配的内存,默认是物理内存的1/64 # -Xmx:最大分配的内存,默认是物理内存的1/4 #* 默认空余堆内存小于 40% 时,JVM就会增大堆直到 -Xmx 的最大限制;空余堆内存大于 70% 时,JVM会减少堆直到 -Xms 的最小限制。 #* 因此服务器一般设置 -Xms、-Xmx 相等以避免在每次 GC 后调整堆的大小。对象的堆内存由称为垃圾回收器的自动内存管理系统回收。 非堆内存分配: # -XX:PermSize:设置非堆内存初始值,默认是物理内存的1/64; # -XX:MaxPermSize:设置最大非堆内存的大小,默认是物理内存的1/4。 # -Xmn2G:设置年轻代大小为2G。 # -XX:SurvivorRatio:设置年轻代中 Eden 区与 Survivor 区的比值。 == 参考 == <references/>
返回至“
【面试:JVM】
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
大陆简体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
笔记
服务器
数据库
后端
前端
工具
《To do list》
日常
阅读
电影
摄影
其他
Software
Windows
WIKIOE
所有分类
所有页面
侧边栏
站点日志
工具
链入页面
相关更改
特殊页面
页面信息