`
jin8000608172
  • 浏览: 140910 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

java之jvm学习笔记十三(jvm基本结构)

阅读更多
引用

http://blog.csdn.net/yfqnihao/article/details/8289363


引用

http://www.importnew.com/1486.html

欢迎装载请说明出处:http://blog.csdn.net/yfqnihao
                      这一节,主要来学习jvm的基本结构,也就是概述。说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成图形,所以只要你有耐心,仔细,认真,并发挥你的想象力,这一章之后你会充满自信。当然,不是说看完本章,就对jvm了解了,jvm要学习的知识实在是非常的多。在你看完本节之后,后续我们还会来学jvm的细节,但是如果你在学习完本节的前提下去学习,再学习其他jvm的细节会事半功倍。
                      为了让你每一个知识点都有迹可循,希望你按照我的步骤一步步继续。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                      知识点1:什么是java虚拟机(你以为你知道,如果你看我下面的例子,你会发现你其实不知道)
                       第一步:先来写一个类: 
[java] view plaincopy
package test; 
 
public class JVMTestForJava { 
    public static void main(String[] args) throws InterruptedException { 
        Thread.sleep(10000000); 


                       第二步:cmd窗口输入:java test.JVMTestForJava
                       第三步:打开任务管理器-进程

你看到一个叫java.exe的程序没有,是滴这个就是java的虚拟机,java xxx这个命令就是用来启动一个java虚拟机,而main函数就是一个java应用的入口,main函数被执行时,java虚拟机就启动了。好了ctrl+c结束你的jvm。
                        第四步:打开你的ecplise,右键run application,再run application一次
                        第五步:打开任务管理器-进程

好了,我已经圈出来了,有两个javaw.exe,为什么会有两个?因为我们刚才运行了两次run application。这里我是要告诉你,一个java的application对应了一个java.exe/javaw.exe(java.exe和javaw.exe你可以把它看成java的虚拟机,一个有窗口界面一个没有)。你运行几个application就有几个java.exe/javaw.exe。或者更加具体的说,你运行了几个main函数就启动了几个java应用,同时也启动了几个java的虚拟机。
                        知识点1总结:
                         什么是java虚拟机,什么是java的虚拟机实例?java的虚拟机相当于我们的一个java类,而java虚拟机实例,相当我们new一个java类,不过java虚拟机不是通过new这个关键字而是通过java.exe或者javaw.exe来启动一个虚拟机实例。
                        看了上面我的描述方式,你觉得如何?概念需要背吗?如果你对我的笔记有信心,继续看下去吧!
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                        知识点2:jvm的生命周期
                         基本上学习一种容器(更具体的说我们在学习servlet的时候),我们都要学习它的生命周期。那么jvm的生命周期如何,我一惯不喜欢丢概念,所以来实验,实践出真知,老师说过的,对不!
                         第一步:copy我代码
[java] view plaincopy
package test; 
 
public class JVMTestLife { 
    public static void main(String[] args) { 
        new Thread(new Runnable() { 
            @Override 
            public void run() { 
                for(int i=0;i<5;i++){ 
                    try { 
                        Thread.currentThread().sleep(i*10000); 
                        System.out.println("睡了"+i*10+"秒"); 
                    } catch (InterruptedException e) { 
                        System.out.println("干嘛吵醒我"); 
                    } 
                } 
            } 
        }).start();  
         
        for(int i=0;i<50;i++){ 
                System.out.print(i); 
        } 
    } 

                      第二步:ecplise里run application
                      第三步:打开任务管理器-进程,看到一个javaw.exe的虚拟机在跑


                    第四步:查看控制台输出,并观察任务管理器中的javaw.exe什么时候消失
[java] view plaincopy
0 睡了0秒 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 睡了10秒 
睡了20秒 
睡了30秒 
睡了40秒 
这是我ecplise里的输出结果,而如果你观察控制台和任务管理器的javaw.exe会发现,当main函数的for循环打印完的时候,程序居然没有退出,而等到整个new Thread()里的匿名类的run方法执行结束后,javaw.exe才退出。我们知道在c++的win32编程(CreatThread()),main函数执行完了,寄宿线程也跟着退出了,在c#中如果你用线程池(ThreadPool)的话,结论也是如此,线程都跟着宿主进程的结束而结束。但是在java中貌似和我们的认知有很大的出入,这是为什么呢?
                    这是由于java的虚拟机种有两种线程,一种叫叫守护线程,一种叫非守护线程,main函数就是个非守护线程,虚拟机的gc就是一个守护线程。java的虚拟机中,只要有任何非守护线程还没有结束,java虚拟机的实例都不会退出,所以即使main函数这个非守护线程退出,但是由于在main函数中启动的匿名线程也是非守护线程,它还没有结束,所以jvm没办法退出(有没有想干坏事的感觉??)。
                   知识点2总结:java虚拟机的生命周期,当一个java应用main函数启动时虚拟机也同时被启动,而只有当在虚拟机实例中的所有非守护进程都结束时,java虚拟机实例才结束生命。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                 知识点三:java虚拟机的体系结构(无奈,我怀着悲痛心情告诉你,我们必须来一些概念,别急,咱有图)


看到这个图没,名词不是普通滴多,先来看看哪些名词我们之前是说过的,执行引擎(笔记一),类装载器(笔记二),java栈(笔记十一)。
在了解jvm的结构之前,我们有必要先来了解一下操作系统的内存基本结构,这段可不能跳过,它会有助于消化上面的那个图哦!好先来看图
操作系统内存布局:

那么jvm在操作系统中如何表示的呢?
操作系统中的jvm

为什么jvm的内存是分布在操作系统的堆中呢??因为操作系统的栈是操作系统管理的,它随时会被回收,所以如果jvm放在栈中,那java的一个null对象就很难确定会被谁回收了,那gc的存在就一点意义都莫有了,而要对栈做到自动释放也是jvm需要考虑的,所以放在堆中就最合适不过了。
操作系统+jvm的内存简单布局


从上图中,你有没有发现什么规律,jvm的内存结构居然和操作系统的结构惊人的一致,你能不能给他们对号入座?还不能,没关系,再来看一个图,我帮你对号入座。看我下面红色的标注

                     从这个图,你应该不难发现,原来jvm的设计的模型其实就是操作系统的模型,基于操作系统的角度,jvm就是个该死的java.exe/javaw.exe,也就是一个应用,而基于class文件来说,jvm就是个操作系统,而jvm的方法区,也就相当于操作系统的硬盘区,所以你知道我为什么喜欢叫他permanent区吗,因为这个单词是永久的意思,也就是永久区,我们的磁盘就是不断电的永久区嘛,是一样的意思啊,多好对应啊。而java栈和操作系统栈是一致的,无论是生长方向还是管理的方式,至于堆嘛,虽然概念上一致目标也一致,分配内存的方式也一直(new,或者malloc等等),但是由于他们的管理方式不同,jvm是gc回收,而操作系统是程序员手动释放,所以在算法上有很多的差异,gc的回收算法,估计是jvm里面的经典啊,后面我们也会一点点的学习的,不要着急。
                     有没有突然自信的感觉?如果你对我的文章有自信,我们再继续,还是以图解的方式,我还是那一句,对于概念我绝对有信心让它在你脑子里根深蒂固。
                     看下面的图。

                        将这个图和上面的图对比多了什么?没错,多了一个pc寄存器,我为什么要画出来,主要是要告诉你,所谓pc寄存器,无论是在虚拟机中还是在我们虚拟机所寄宿的操作系统中功能目的是一致的,计算机上的pc寄存器是计算机上的硬件,本来就是属于计算机,(这一点对于学过汇编的同学应该很容易理解,有很多的寄存器eax,esp之类的32位寄存器,jvm里的寄存器就相当于汇编里的esp寄存器),计算机用pc寄存器来存放“伪指令”或地址,而相对于虚拟机,pc寄存器它表现为一块内存(一个字长,虚拟机要求字长最小为32位),虚拟机的pc寄存器的功能也是存放伪指令,更确切的说存放的是将要执行指令的地址,它甚至可以是操作系统指令的本地地址,当虚拟机正在执行的方法是一个本地方法的时候,jvm的pc寄存器存储的值是undefined,所以你现在应该很明确的知道,虚拟机的pc寄存器是用于存放下一条将要执行的指令的地址(字节码流)。
                        再对上面的图扩展,这一次,我们会稍微的深入一点,放心啦,不会很深入,我们的目标是浅显易懂,好学易记嘛!看下面的图。
        
多了什么?没错多了一个classLoader,其实这个图是要告诉你,当一个classLoder启动的时候,classLoader的生存地点在jvm中的堆,然后它会去主机硬盘上将A.class装载到jvm的方法区,方法区中的这个字节文件会被虚拟机拿来new A字节码(),然后在堆内存生成了一个A字节码的对象,然后A字节码这个内存文件有两个引用一个指向A的class对象,一个指向加载自己的classLoader,如下图。

那么方法区中的字节码内存块,除了记录一个class自己的class对象引用和一个加载自己的ClassLoader引用之外,还记录了什么信息呢??我们还是看图,然后我会讲给你听,听过一遍之后一辈子都不会忘记。

你仔细将这个字节码和我们的类对应,是不是和一个基本的java类惊人的一致?下面你看我贴出的一个类的基本结构。
[java] view plaincopy
package test;import java.io.Serializable;public final class ClassStruct extends Object implements Serializable {//1.类信息 
//2.对象字段信息 
private String name; 
private int id; 
  
//4.常量池 
public final int CONST_INT=0; 
    public final String CONST_STR="CONST_STR"; 
     
    //5.类变量区 
    public static String static_str="static_str"; 
     
  
//3.方法信息 
public static final String getStatic_str ()throws Exception{ 
  return ClassStruct.static_str; 
}} 
你将上面的代码注解和上面的那个字节码码内存块按标号对应一下,有没有发现,其实内存的字节码块就是完整的把你整个类装到了内存而已。
所以各个信息段记录的信息可以从我们的类结构中得到,不需要你硬背,你认真的看过我下面的描述一遍估计就不可能会忘记了:
       1.类信息:修饰符(public final)
                        是类还是接口(class,interface)
                        类的全限定名(Test/ClassStruct.class)
                        直接父类的全限定名(java/lang/Object.class)
                        直接父接口的权限定名数组(java/io/Serializable)
      也就是 public final class ClassStruct extends Object implements Serializable这段描述的信息提取
       2.字段信息:修饰符(pirvate)
                            字段类型(java/lang/String.class)
                            字段名(name)
        也就是类似private String name;这段描述信息的提取
       3.方法信息:修饰符(public static final)
                          方法返回值(java/lang/String.class)
                          方法名(getStatic_str)
                          参数需要用到的局部变量的大小还有操作数栈大小(操作数栈我们后面会讲)
                          方法体的字节码(就是花括号里的内容)
                          异常表(throws Exception)
       也就是对方法public static final String getStatic_str ()throws Exception的字节码的提取
       4.常量池:
                    4.1.直接常量:
                                   1.1CONSTANT_INGETER_INFO整型直接常量池public final int CONST_INT=0;
                                   1.2CONSTANT_String_info字符串直接常量池   public final String CONST_STR="CONST_STR";
                                   1.3CONSTANT_DOUBLE_INFO浮点型直接常量池
                                   等等各种基本数据类型基础常量池(待会我们会反编译一个类,来查看它的常量池等。)
                     4.2.方法名、方法描述符、类名、字段名,字段描述符的符号引用
            也就是所以编译器能够被确定,能够被快速查找的内容都存放在这里,它像数组一样通过索引访问,就是专门用来做查找的。
            编译时就能确定数值的常量类型都会复制它的所有常量到自己的常量池中,或者嵌入到它的字节码流中。作为常量池或者字节码流的一部分,编译时常量保存在方法区中,就和一般的类变量一样。但是当一般的类变量作为他们的类型的一部分数据而保存的时候,编译时常量作为使用它们的类型的一部分而保存
      5.类变量:
                  就是静态字段( public static String static_str="static_str";)
                  虚拟机在使用某个类之前,必须在方法区为这些类变量分配空间。
      6.一个到classLoader的引用,通过this.getClass().getClassLoader()来取得为什么要先经过class呢?思考一下,然后看第七点的解释,再回来思考
      7.一个到class对象的引用,这个对象存储了所有这个字节码内存块的相关信息。所以你能够看到的区域,比如:类信息,你可以通过this.getClass().getName()取得
          所有的方法信息,可以通过this.getClass().getDeclaredMethods(),字段信息可以通过this.getClass().getDeclaredFields(),等等,所以在字节码中你想得到的,调用的,通过class这个引用基本都能够帮你完成。因为他就是字节码在内存块在堆中的一个对象
      8.方法表,如果学习c++的人应该都知道c++的对象内存模型有一个叫虚表的东西,java本来的名字就叫c++- -,它的方法表其实说白了就是c++的虚表,它的内容就是这个类的所有实例可能被调用的所有实例方法的直接引用。也是为了动态绑定的快速定位而做的一个类似缓存的查找表,它以数组的形式存在于内存中。不过这个表不是必须存在的,取决于虚拟机的设计者,以及运行虚拟机的机器是否有足够的内存
--------------------------------------------------------------------------------------忽略下面虚线间的废话-------------------------------------------------------------------------------------------------------------------------
大哭好了,还剩这么多没讲过。不过不要急,我一向提倡,学到哪里讲到哪里,看到哪里。所以没有学到的概念,让他随风去。
但是我还是会来串一下思路滴:
                 首先,当一个程序启动之前,它的class会被类装载器装入方法区(不好听,其实这个区我喜欢叫做Permanent区),执行引擎读取方法区的字节码自适应解析,边解析就边运行(其中一种方式),然后pc寄存器指向了main函数所在位置,虚拟机开始为main函数在java栈中预留一个栈帧(每个方法都对应一个栈帧),然后开始跑main函数,main函数里的代码被执行引擎映射成本地操作系统里相应的实现,然后调用本地方法接口,本地方法运行的时候,操纵系统会为本地方法分配本地方法栈,用来储存一些临时变量,然后运行本地方法,调用操作系统APIi等等。        
好吧,你听晕了,我知道,先记住这段话的位置,等某年某月我提醒你回来看,你就焕然大悟了,现在你只需要走马观花咯!!!
--------------------------------------------------------------------------------------忽略下面虚线间的废话-------------------------------------------------------------------------------------------------------------------------
好了,这一节的内容实在够多了,所以我打算把它拆解一下,剩下的内容放到下一节,下一节我们会来学习虚拟机的堆栈,和堆。
分享到:
评论

相关推荐

    java之jvm学习笔记五(实践写自己的类装载器)

    这个“java之jvm学习笔记五(实践写自己的类装载器)”很可能是对这一主题的详细探讨。 类装载器在Java中的主要职责是动态加载类到JVM中。Java的类装载器分为三个基本层次:启动类装载器(Bootstrap ClassLoader)、...

    java之jvm学习笔记十一(访问控制器)-源码

    本文将深入探讨JVM中的访问控制器,主要基于“java之jvm学习笔记十一(访问控制器)-源码”这一主题,以及相关的源码分析。 首先,我们得了解Java的安全模型。Java安全模型基于一种称为安全管理器(SecurityManager)...

    java之jvm学习笔记八(实践对jar包的代码签名)

    Java的JVM(Java Virtual Machine)是Java程序运行的基础,它负责解释和执行字节码。在实际开发中,为了确保代码的安全性、完整性和防止恶意篡改,我们需要对Java的JAR(Java Archive)包进行代码签名。本文将深入...

    java之jvm学习笔记十而(访问控制器的栈校验机制)

    Java虚拟机(JVM)是Java程序运行的核心,它负责解释和执行字节码,确保程序的正确运行。在JVM的设计中,访问控制器扮演着关键角色,它负责执行严格的类型检查,确保代码的安全性。本文将深入探讨“访问控制器的栈...

    JVM学习笔记(一)

    ### JVM学习笔记(一) #### 一、JVM概述与工具使用 JVM(Java Virtual Machine)是Java语言的核心组成部分之一,它为Java程序提供了一个跨平台的运行环境。本篇学习笔记主要介绍如何利用一系列工具来查看和监控JVM...

    java之jvm学习笔记九(策略文件)

    本篇笔记将深入探讨Java策略文件的概念、结构以及如何使用。 策略文件是Java安全模型的一部分,它定义了哪些代码可以执行特定的操作,例如访问网络、读写文件或运行系统命令。这些限制通常基于代码的来源,比如是否...

    java之jvm学习笔记十而(访问控制器的栈校验机制)-步骤2源码

    本篇学习笔记将深入探讨这一机制,主要聚焦于步骤2的源码分析。在Java的世界里,类型安全至关重要,尤其是在多线程环境下,防止非法的类型访问和操作能避免潜在的安全风险和程序崩溃。 栈校验机制主要是为了执行...

    java之jvm学习笔记十一(访问控制器) -源码

    本文将深入探讨JVM中的访问控制器,并通过分析"java之jvm学习笔记十一(访问控制器) -源码"中的`MySecurityManager`来进一步理解其工作原理。 访问控制器的主要任务是对类、方法和字段的访问进行限制,防止恶意代码...

    java之jvm学习笔记十二(访问控制器的栈校验机制)

    本篇学习笔记将深入探讨这一主题,主要关注Java字节码的执行过程以及如何通过栈校验来防止非法操作。 Java字节码是由Java编译器生成的中间代码,它在JVM上运行前会经过类加载器的验证,其中栈校验是验证的一部分。...

    JVM学习笔记.docx

    本篇JVM学习笔记主要涵盖了以下几个核心知识点: 1. **运行时数据区**: - **程序计数器**:记录当前线程执行的字节码的行号,用于线程恢复执行时跳转到正确位置。 - **Java虚拟机栈**:每个方法执行时创建的栈帧...

    jVM学习笔记.ppt

    JVM的学习可以从其基本结构、代码编译和执行过程,以及内存管理和垃圾回收机制三个方面进行深入探讨。 首先,JVM的基本结构分为逻辑结构和物理结构。逻辑结构主要包括Java源码编译器、JVM执行引擎、类加载器等组件...

    JVM学习笔记

    ### JVM学习笔记 #### JVM内存模型 (JMM) JVM内存模型主要分为以下几个部分: - **Java堆**:这是所有线程共享的一块区域,在虚拟机启动时创建。主要用于存放对象实例,几乎所有的对象实例都在这里分配内存。 - *...

    JVM 学习笔记(Java虚拟机)

    **JVM学习笔记(Java虚拟机)** Java虚拟机(JVM)是Java语言的核心组成部分,它是Java程序运行的平台,负责解释和执行字节码。深入理解JVM对于优化Java应用程序性能至关重要。本笔记将从以下几个方面详细介绍JVM:...

    Java,JVM相关笔记的代码知识

    这份JVM相关的笔记包含了深入理解JVM内部工作机制的关键代码资源,是学习和优化Java应用程序的重要参考资料。下面,我们将深入探讨Java与JVM的相关知识点。 1. **类加载机制**:JVM通过类加载器(ClassLoader)将...

    Java JDK7学习笔记-光盘

    《Java JDK7学习笔记》是作者多年来教学实践经验的总结,汇集了教学过程中学生在学习java时遇到的概念、操作、应用或认证考试等问题及解决方案。《Java JDK7学习笔记》针对java se 7新功能全面改版,无论是章节架构...

    Java 学习笔记Java学习笔记

    Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems(现为Oracle公司的一部分)于1995年发布。...Java学习笔记涵盖了这些核心知识点,通过深入学习和实践,你可以逐步掌握Java编程,并应用于实际项目开发中。

    jvm视频及笔记

    "jvm视频及笔记"这个资源显然是一份全面学习JVM的材料,结合了视频教程和书面笔记,帮助学习者深入理解JVM的工作原理及其在实际开发中的应用。 JVM的学习可以从以下几个重要的知识点开始: 1. **JVM架构**:JVM...

    JVM学习资料+笔记

    这个资料包不仅涵盖了理论知识,还包含个人的学习笔记,对于学习和掌握JVM的各个方面都将大有裨益。无论是初学者还是经验丰富的开发者,都可以从中找到提升自己技能的宝贵资源。通过深入学习和实践,可以更好地理解...

Global site tag (gtag.js) - Google Analytics