我们都知道java文件是不能直接运行在操作系统上的,但是它本身又具备着跨平台的特性这主要的原因就是因为java虚拟机的存在。Java 源文件,通过编译器,能够生产相应的.Class 文件,也就是字节码文件, 而字节码文件又通过 Java 虚拟机中的解释器,编译成特定机器上的机器码 。
先上一张图
接着我们照着图示来梳理一遍jvm里的一些基本原理:
上面的描述中我们大概已经清楚了一些关于java代码的执行过程,.Java文件要被编译成.class文件通过classLoader来加载,最后由执行器来执行编译后的文件。
1):ClassLoader的加载过程:
我们通常将ClassLoader的过程叫做双亲委派机制,下面来梳理一下,加载器主要有四个,启动类加载器(Bootstrap ClassLoader),扩展类加载器(Extension ClassLoader)),应用程序类加载器(Application ClassLoader)和用户自定义的加载器。由启动类加载器加载java_home\lib下核心包,由扩展类加载器加载java_home\lib\ext下的扩展包,应用程序加载器加载了用户自定义路径下的类库。双亲委派机制就是每次收到类加载的请求都会把请求交给父类完成,只有当父类无法完成的时候子类的加载器才会尝试自己器加载它。这样做的目的有两个:1.保证了我们的对象都是唯一的。2.为了保证程序的安全, 系统类同名的类一定是系统自己提供的,比如你自己写的String类是不会被加载到的。
2):内存空间:
JVM内存区域可以划分为三个部分,线程私有,线程共享和直接内存。线程私有的包括(程序计数器,栈,本地方法区),线程共享的包括(堆和方法区)。线程私有的数据区域依赖与用户线程,随着用户线程启动而启动,随着用户线程的结束而结束。
程序计数器是一块很小的内存空间,是每一个线程所独立持有的,因此这个内存区域是不存在OOM的问题的。
栈是一个描述java方法执行的内存模型,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。一个方法从开始调用到执行结束的过程就是这个栈帧从入栈到出栈的过程。
堆也称为运行时数据区,是一块线程共享的区域。我们的对象和数组都是保存在堆中,堆的发展历程中有很多的优化,我们现在虚拟机使用的基本多是分代收集,也就是将堆分为了两个部分,新生代和老年代。在新生代中又分为eden,from 和to三个区。下一节细说
方法区又称为永久代,存贮的是类信息,常量和静态变量,这部分内存在GC中会被回收的主要是常量池。