Division and the role of java virtual machine memory area of a Detailed run-time data area

First, the run-time data area

       What is the runtime data area of ​​it, see the figure to know, today's emphasis on talk around this picture.

                

      1, the program counter (register)           

            Line number indicator bytecode currently executing thread

            Bytecode interpreter counter control is completed jobs depend

            By executing thread line number is recorded, so that the thread switch turns counter independently of each other between the threads

            Thread private life cycle with the same thread, with the JVM startup born, JVM close to death

            Java thread execution method, recording the virtual machine bytecode instruction address that is being executed

            Nativan thread execution method, counter record is empty (Undefined)

            The only areas not specified in any case OutOfMemoryError Java Virtual Machine Specification

        In this one, a lot of it does not matter do not understand, we learn too many threads, there are two threads, one thread can be suspended to allow other threads to run, and then when they have gained cpu and other resources, and can run from where you left off, so why can pause to remember the location, which rely on the program counter, through this example, probably look at the functionality of the program counter.

 

      2, native method stacks

            Do not know if you read the source code is not, read all should know that a lot of algorithm or a function of the realization, are encapsulated into the local java method, the program directly through the local method call on the line, native method stacks is used to store in this way, the code that implements this functionality may be C may also be C ++, anyway, it is not necessarily a java implementation.

 

      Two are not the focus of what we should learn from the above, the next three is the key.

 

      3, the virtual machine stack

          Everyone should understand this, it is now to go into detail, the virtual machine is described stack memory model Java method executed: Each method creates a stack frame memory for storing local variable table while performing, the operand table dynamic connection information for export, each method execution until completion of the procedure is called, a corresponding stack frame to push the stack in a virtual machine process stack. How to understand the words? When performing such a class (main method in class), to execute the main method will create a stack frame of the main method is then applied to the virtual machine stack, the stack frame which will store the various main methods local variables, object references and other things. Figure

                    

          When calling another method in the main method, there will be another method stack frame stack into the virtual machine, when the method call is over, popped, then the main method is at the top, then continue until the end, then the main method is also popped stack frame, the program is finished. In short stack in a virtual machine that has a lot of push a stack frame the stack, the stack frame stored in the city of some variable names and other things, so we usually say that the stack is stored in a number of local variables as local variables is in the method. That is, in the stack frame, that is to say over.

 

        Above that three threads are not shared, which is part of the memory, each thread unique, will not allow access to other threads, the next two threads that share, and also there will be thread-safety issues .

 

     4, heap

        All threads of a shared memory area. The largest piece of memory in the Java virtual machine management, since the sole purpose of that area of ​​memory that is stored object instance. Almost all object instances of where to allocate memory, that is, we usually say that the new object, which will open up a heap of memory to store some of the information object, such as attribute it or something. Meanwhile heap garbage collector is also a major area of ​​management. So often referred to as "GC heap", under the virtual machine's garbage collection and other article to explain. Stored in a stack on the point of speaking local reference variable points most of the heap will be stored.

 

    5, and wherein the method area runtime constant pool

        和堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、和编译器编译后的代码(也就是存储字节码文件。.class)等数据,这里可以看到常量也会在方法区中,是因为方法区中有一个运行时常量池,为什么叫运行时常量池,因为在编译后期生成的是各种字面量(字面量的意思就是值,比如int i=3,这个3就是字面量的意思)和符号引用,这些是存放在一个叫做常量池(这个常量池是在字节码文件中)的地方,当类加载进入方法区时,就会把该常量池中的内容放入运行时常量池中。这里要注意,运行时常量池和常量池,不要搞混淆了,字节码文件中也有常量池,在后面的章节会详细讲解这个东西。现在只需要知道方法区中有一个运行时常量池,就是用来存放常量的。还有一点,运行时常量池不一定就一定要从字节码常量池中拿取常量,可能在程序运行期间将新的常量放入池中,比如String.intern()方法,这个方法的作用就是:先从方法区的运行时常量池中查找看是否有该值,如果有,则返回该值的引用,如果没有,那么就会将该值加入运行时常量池中。

        

 

二、练习。画内存图。

        平常分析中用到的最多还是堆、虚拟机栈和方法区。

        例如:看下面这段程序,然后画出内存分析图        

  

       最主要是看我的分析过程,这个图由于要显示出动态弹栈画不了,所以只能够那样画一下了。

        1、首先运行程序,Demo1_car.java就会变为Demo1_car.class,将Demo1_car.class加入方法区,检查是否字节码文件常量池中是否有常量值,如果有,那么就加入运行时常量池

        2、遇到main方法,创建一个栈帧,入虚拟机栈,然后开始运行main方法中的程序

        3、Car c1 = new Car(); 第一次遇到Car这个类,所以将Car.java编译为Car.class文件,然后加入方法区,跟第一步一样。然后new Car()。就在堆中创建一块区域,用于存放创建出来的实例对象,地址为0X001.其中有两个属性值 color和num。默认值是null 和 0

        4、然后通过c1这个引用变量去设置color和num的值,

        5、调用run方法,然后会创建一个栈帧,用来装run方法中的局部变量的,入虚拟机栈,run方法中就打印了一句话,结束之后,该栈帧出虚拟机栈。又只剩下main方法这个栈帧了

        6、接着又创建了一个Car对象,所以又在堆中开辟了一块内存,之后就是跟之前的步骤一样了。

 

 

    这样就分析结束了,在脑袋中就应该有一个大概的认识对堆、虚拟机栈、和方法区。注意这个方法区的名字,并不是就单单装方法的,能装很多东西。

 

 

  这个只是一个简单的分析,可以再讲具体一点,1、创建对象,在堆中开辟内存时是如何分配内存的?2、对象引用是如何找到我们在堆中的对象实例的?通过这两个问题来加深我们的理解。

 1、创建对象,在堆中开辟内存时是如何分配内存的?

       两种方式:指针碰撞和空闲列表。我们具体使用的哪一种,就要看我们虚拟机中使用的是什么了。

       指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存度放一边,空闲的内存放另一边,中间放着一个指针作为分界点的指示器,所分配内存就仅仅是把哪个指针向空闲空间那边挪动一段与对象大小相等的举例,这种分配方案就叫指针碰撞

       空闲列表:有一个列表,其中记录中哪些内存块有用,在分配的时候从列表中找到一块足够大的空间划分给对象实例,然后更新列表中的记录。这就叫做空闲列表

 2、对象引用是如何找到我们在堆中的对象实例的?     

       这个问题也可以称为对象的访问定位问题,也有两种方式。句柄访问和直接指针访问。 画两张图就明白了。

       句柄访问:Java堆中会划分出一块内存来作为句柄池,引用变量中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息

              

        解释图:在栈中有一个引用变量指向句柄池中一个句柄的地址,这个句柄又包含了两个地址,一个对象实例数据,一个是对象类型数据(这个在方法区中,因为类字节码文件就放在方法区中),

      

      直接指针访问:引用变量中存储的就直接是对象地址了,如图所示

            

        解释:在堆中就不会分句柄池了,直接指向了对象的地址,对象中包含了对象类型数据的地址。

 

      区别:这两种各有各的优势,

          使用句柄来访问的最大好处就是引用变量中存储的是稳定的句柄地址,对象被移动(在垃圾收集时移动对象是很普通的行为)时就会改变句柄中实力数据指针,但是引用变量所指向的地址不用改变。

          而使用直接指针访问方式最大的好处就是速度更快,节省了一次指针定位的时间开销,但是在对象被移动时,又需要改变引用变量的地址。在我们上面分析的例子中,就是使用的直接指针访问的方式。

       什么叫运行时数据区呢,看下图就知道了,今天的重点就围绕这张图讲。

                

      1、程序计数器(寄存器)           

            当前线程所执行的字节码行号指示器

            字节码解释器工作依赖计数器控制完成

            通过执行线程行号记录,让线程轮流切换各条线程之间计数器互不影响

            线程私有,生命周期与线程相同,随JVM启动而生,JVM关闭而死

            线程执行Java方法时,记录其正在执行的虚拟机字节码指令地址

            线程执行Nativan方法时,计数器记录为空(Undefined)

            唯一在Java虚拟机规范中没有规定任何OutOfMemoryError情况区域

        在这其中,很多不理解的没关系,我们学过多线程,有两个线程,其中一个线程可以暂停使用,让其他线程运行,然后等自己获得cpu资源时,又能从暂停的地方开始运行,那么为什么能够记住暂停的位置的,这就依靠了程序计数器, 通过这个例子,大概了解一下程序计数器的功能。

 

      2、本地方法栈

            不知道大家看过源码没有,看过的都应该知道,很多的算法或者一个功能的实现,都被java封装到了本地方法中,程序直接通过调用本地的方法就行了,本地方法栈就是用来存放这种方法的,实现该功能的代码可能是C也可能是C++,反正不一定就是java实现的。

 

      上面两个不是我们所要学习的重点,接下来三个才是重点。

 

      3、虚拟机栈

          这个大家都应该有所了解,现在来细讲它,虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧用来存放存储局部变量表、操作数表、动态连接、方法出口等信息,每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。    这个话怎么理解呢?比如执行一个类(类中有main方法)时,执行到main方法,就会把为main方法创建一个栈帧,然后在加到虚拟机栈中,栈帧中会存放这main方法中的各种局部变量,对象引用等东西。如图

                    

          当在main方法中调用别的方法时,就会有另一个方法的栈帧入虚拟机栈,当该方法调用完了之后,弹栈,然后main方法处于栈顶,就继续执行,直到结束,然后main方法栈帧也弹栈,程序就结束了。总之虚拟机栈中就是有很多个栈帧的入栈出栈,栈帧中存放的都市一些变量名等东西,所以我们平常说栈中存放的是一些局部变量,因为局部变量就是在方法中。也就是在栈帧中,就是这样说过来的。

 

        以上说的三个都是线程不共享的,也就是这部分内存,每个线程独有,不会让别的线程访问到,接下来的两个就是线程共享了,也就会出现线程安全问题。

 

     4、堆

        所有线程共享的一块内存区域。Java虚拟机所管理的内存中最大的一块,因为该内存区域的唯一目的就是存放对象实例。几乎所有的对象实例度在这里分配内存,也就是通常我们说的new对象,该对象就会在堆中开辟一块内存来存放对象中的一些信息,比如属性呀什么的。同时堆也是垃圾收集器管理的主要区域。因此很多时候被称为"GC堆",虚拟机的垃圾回收机制等下一篇文章来讲解。 在上一点讲的栈中存放的局部引用变量所指向的大多数度会在堆中存放。

 

    5、方法区和其中的运行时常量池

        和堆一样,是各个线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、和编译器编译后的代码(也就是存储字节码文件。.class)等数据,这里可以看到常量也会在方法区中,是因为方法区中有一个运行时常量池,为什么叫运行时常量池,因为在编译后期生成的是各种字面量(字面量的意思就是值,比如int i=3,这个3就是字面量的意思)和符号引用,这些是存放在一个叫做常量池(这个常量池是在字节码文件中)的地方,当类加载进入方法区时,就会把该常量池中的内容放入运行时常量池中。这里要注意,运行时常量池和常量池,不要搞混淆了,字节码文件中也有常量池,在后面的章节会详细讲解这个东西。现在只需要知道方法区中有一个运行时常量池,就是用来存放常量的。还有一点,运行时常量池不一定就一定要从字节码常量池中拿取常量,可能在程序运行期间将新的常量放入池中,比如String.intern()方法,这个方法的作用就是:先从方法区的运行时常量池中查找看是否有该值,如果有,则返回该值的引用,如果没有,那么就会将该值加入运行时常量池中。

        

 

二、练习。画内存图。

        平常分析中用到的最多还是堆、虚拟机栈和方法区。

        例如:看下面这段程序,然后画出内存分析图        

  

       最主要是看我的分析过程,这个图由于要显示出动态弹栈画不了,所以只能够那样画一下了。

        1、首先运行程序,Demo1_car.java就会变为Demo1_car.class,将Demo1_car.class加入方法区,检查是否字节码文件常量池中是否有常量值,如果有,那么就加入运行时常量池

        2、遇到main方法,创建一个栈帧,入虚拟机栈,然后开始运行main方法中的程序

        3、Car c1 = new Car(); 第一次遇到Car这个类,所以将Car.java编译为Car.class文件,然后加入方法区,跟第一步一样。然后new Car()。就在堆中创建一块区域,用于存放创建出来的实例对象,地址为0X001.其中有两个属性值 color和num。默认值是null 和 0

        4、然后通过c1这个引用变量去设置color和num的值,

        5、调用run方法,然后会创建一个栈帧,用来装run方法中的局部变量的,入虚拟机栈,run方法中就打印了一句话,结束之后,该栈帧出虚拟机栈。又只剩下main方法这个栈帧了

        6、接着又创建了一个Car对象,所以又在堆中开辟了一块内存,之后就是跟之前的步骤一样了。

 

 

    这样就分析结束了,在脑袋中就应该有一个大概的认识对堆、虚拟机栈、和方法区。注意这个方法区的名字,并不是就单单装方法的,能装很多东西。

 

 

  这个只是一个简单的分析,可以再讲具体一点,1、创建对象,在堆中开辟内存时是如何分配内存的?2、对象引用是如何找到我们在堆中的对象实例的?通过这两个问题来加深我们的理解。

 1、创建对象,在堆中开辟内存时是如何分配内存的?

       两种方式:指针碰撞和空闲列表。我们具体使用的哪一种,就要看我们虚拟机中使用的是什么了。

       指针碰撞:假设Java堆中内存是绝对规整的,所有用过的内存度放一边,空闲的内存放另一边,中间放着一个指针作为分界点的指示器,所分配内存就仅仅是把哪个指针向空闲空间那边挪动一段与对象大小相等的举例,这种分配方案就叫指针碰撞

       空闲列表:有一个列表,其中记录中哪些内存块有用,在分配的时候从列表中找到一块足够大的空间划分给对象实例,然后更新列表中的记录。这就叫做空闲列表

 2、对象引用是如何找到我们在堆中的对象实例的?     

       这个问题也可以称为对象的访问定位问题,也有两种方式。句柄访问和直接指针访问。 画两张图就明白了。

       句柄访问:Java堆中会划分出一块内存来作为句柄池,引用变量中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息

              

        解释图:在栈中有一个引用变量指向句柄池中一个句柄的地址,这个句柄又包含了两个地址,一个对象实例数据,一个是对象类型数据(这个在方法区中,因为类字节码文件就放在方法区中),

      

      直接指针访问:引用变量中存储的就直接是对象地址了,如图所示

            

        解释:在堆中就不会分句柄池了,直接指向了对象的地址,对象中包含了对象类型数据的地址。

 

      区别:这两种各有各的优势,

          使用句柄来访问的最大好处就是引用变量中存储的是稳定的句柄地址,对象被移动(在垃圾收集时移动对象是很普通的行为)时就会改变句柄中实力数据指针,但是引用变量所指向的地址不用改变。

          而使用直接指针访问方式最大的好处就是速度更快,节省了一次指针定位的时间开销,但是在对象被移动时,又需要改变引用变量的地址。在我们上面分析的例子中,就是使用的直接指针访问的方式。

Guess you like

Origin www.cnblogs.com/wyb2126/p/11479527.html