学习java有一段时间,一直挺好奇java底层是怎么实现的,以及java语言特性的优点。因此自己作下总结,如果本文有错误处请各位看官批评指出,我要及时修正我的一些理解。
跨平台
首先对于java,之所以能够跨平台运行,依赖于底层的jvm实现机制。所有的代码写完后,都会翻译成jvm所能识别的字节进行存取。jvm编码都是用Unicode。当需要加载的时候,就会把字节转为平台所能懂得语言。因此一个java能够在不同平台运行,只需要配置好jvm,将你的java文件拿到不同平台运行就可以了。
垃圾回收
同时,jvm有垃圾回收机制,不像c++一样用指针,进行显示回收,容易造成内存泄露。jvm会判断对象是否存活对对象进行回收。主要有两种方法:可达性分析和引用计数算法。说下两者区别:
1. 可达性分析:通过一些GC Roots的对象作为索引,向下搜索,搜索所有走过的路径称为“引用链”。当对象没有被任何引用链所连接时,则这个对象时不可到达的。就被判断为是可以回收的对象。
2. 引用计数算法:给每个对象添加一个引用计数器。被引用的时候,计数器加一;当不被引用的时候,计数器减一。当计数器为0时,表示可以被回收。这个方法虽然简单易懂,但是无法解决对象间相互引用的情况。
内存分配
JVM在类加载时会把内存分配成各个区域。这部分区域属于运行时数据区。区域主要分为5个部分,分别为:程序计数区,JAVA虚拟机栈,本地方法区,JAVA堆,方法区。以下谈谈我对这几个区域的理解
线程私有的
1. 程序计数区:JVM实现多线程,是通过线程轮流切换并分配处理器。一个处理器只会执行一个线程的指令。为了让线程能够安全顺利地执行,为每个线程都添加了私有的程序计数器。(这是JVM上唯一一个不会发生OOM异常的区域)
2. JAVA虚拟机栈:它和JAVA堆得区别主要是存储一些基本数据类型和对象的引用。它是为JVM提供JAVA方法。一个方法从调用到执行结束,就对应了一个栈帧在虚拟机中入栈和出栈的整个过程。
3. 本地方法区:同JAVA虚拟机栈类似,主要是提供JVM的Native方法。
所有线程所共有的数据区
1. JAVA堆:new出的对象都是在JAVA堆上,当创建一个对象时,就会在堆上创建相应的java.lang.class文件,在方法区上保存相应类的数据结构,相当于JAVA堆提供了类在方法区上的接口。这部分区域也是GC回收的主要区域。内存分配是不连续的。
2. 方法区:同样是不连续的,存储了类里的常量,静态变量,编译后的代码,被虚拟机加载的类信息等。
值得注意的是,JDK1.7及之后的版本,JVM已经将运行时常量池从方法区中移了出来,在JAVA堆上开辟了一块区域存放运行时常量池。同时JAVA8的到来,也取消了老年代,为其开辟了新的空间成为元空间。具体大家可以百度搜索一下。
JVM类加载的过程
加载过程包括三个部分:
1. 装载
将class文件字节码内容加载到内存,即翻译成jvm能看懂的语言。将类的静态变量、类信息放置在运行时数据区的方法区内,在堆上创建java.lang.class对象,提供方法区的数据入口。
2. 连接
连接包含验证、准备、解析
其中验证是指类的加载信息完全符合JVM规范;
准备是指为static成员变量分配内存,初始化值,这些值是在方法区中进行分配;
解析是指常量池的符号引用替换成字节引用;
3. 初始化
初始化类是通过初始化类的构造器的方法,如果该类有父类,则先初始化该父类构造器方法。
以上,是我关于JVM运行机制的一些理解,欢迎补充或批评指导!