java虚拟机与jvm内存模型

来源:互联网 发布:淘宝 实木写字台图片 编辑:程序博客网 时间:2024/05/23 11:01

1.Java虚拟机本质与编译器由来

       Java虚拟机jvm(一个c/c++程序而已,当然也有其他语法实现的)其实是一个进程虚拟机--上面跑进程的(,普通服务器虚拟机是跑操作系统的),启动一个java程序就是启动一个java虚拟机上面跑这个java程序,这个启动的java虚拟机对操作系统来说其实就是一个(c++)进程,这个进程会调用本地方法(java中叫本地方法,其实就是C/C++的库函数,C/C++汇编语言编写,编译成和处理器相关的机器代码,存储在动态链接库中)

      Java虚拟机怎么跑java程序的呢?类装载器装载java程序编译后的字节码(class文件-与平台无关)和Java API(只有程序执行时需要的class才会被装载)--》执行引擎即时翻译成本地方法调用,调用操作系统资源。运行中Java程序的每一个线程都是一个独立的虚拟机执行引擎的实例。从线程生命周期的开始到结束,它要么在执行字节码,要么执行本地方法。

       最开始第一个编译器怎么来的?最开始人对照机器指令写的程序,编译器也是程序,所以是人直接用二进制写的,就有了汇编,汇编写了B语言编译器-》C语言……机器码生汇编,汇编生B,B生C,C生万物


2.JVM内存模型

1)内存区域划分



   java虚拟机规范中把所管理的内存区域-运行时数据区包括方法区、虚拟机栈、本地方法栈、堆、和程序计数器,前面说的jvm就是个C/C++程序,

C/C++程序能自己管理控制生命周期和垃圾回收是C/C++程序内存四区中的堆区。所以个人认为运行时数据区是在C/C++程序内存四区中的堆区

  •     方法区
          这部分区域线程共享,存储被虚拟机加载的类信息、常量(jdk1.7移到了堆)、静态变量、即使编译器编译后的代码等数据(对应C/C++程序内存四区中的全局区和代码区),虽然java虚拟机规范把方法区描述为对的一个逻辑部分,但它有个别名叫Non-Heap(非堆),目的是应该是与java堆区分。这段区域的垃圾回收主要是常量池和类型的卸载。可以抛出OutOfMemoryError。
           方法区别很多人成为永久代,本质两者不等价,永久代只是HotSpot对方法区的实现,HotSpot的垃圾收集器可以像java堆(新生代和老年代)一样管理这部分内存,省去专门为方法区编写内存管理的工作。但是永久代来实现方法区更容易出现内存溢出(这段内存大小不好估量,设置少了容易导致溢出),JDK 1.7将字符串常量移到了堆区,JDK 1.8将永久代改成元空间,元空间并不在虚拟机中,而是使用本地直接内存。因此,默认情况下,元空间的大小仅受本地内存限制。
  •     虚拟机栈:
           线程私有,方法执行时用于存储方法的局部变量表、操作数据栈、动态链接、方法出出口等信息--通常栈帧。每个方法从调用到结束对应着一个栈帧在虚拟机栈中入栈到出栈。局部变量表存放了编译期可知的基本数据类型(boolean、byte、char、short、int、float、long、double),对象引用和returnAddress(执行一条字节码指令的地址),局部变量表所需的内存空间在编译期间就确定了,方法运行期间不会改变。可以抛出StackOverflowError和OutOfMemoryError
  •     本地方法栈:
           与虚拟机栈相似,只是它是为本地方法服务的。
  •     java堆:
           线程共享,该区域存放几乎所有对象实例,虚拟机规范中描述为所有,但是随着JIT编译器和逃逸分析技术的发展,栈上分配、标量替换将会导致一些微妙变化----不是所有对象都在java堆。可细分为:新生代(enden、survivor)和老年代。可以抛出OutOfMemoryError。

  •     程序计数器:
            线程私有,指向下一条执行命令,在执行本地方法时为空

  •     本地直接内存:不属于jvm运行时数据区,飘离于java堆,个人理解认为一般的堆对象要通过jvm做一次包装加工(如对象头、地址定位转换)由jvm去操作那段内存,管理回收那段内存,直接内存是中间少包装转换,也不受jvm常规垃圾收集器处理。直接内存一般有两种使用方式:
  1. Unsafe:allocateMemory方法申请内存和freeMemory方法用来自己释放内存
  2. DirectBuffer:是对Unsafe的封装,内存释放也帮我们做了 ,不需要我们手动释放内存,在DirectBuffer对象被垃圾回收时会触发直接内存的释放(java的Reference与DirectBuffer的内存回收、DirectBuff释放内存)

2)对象内存布局与访问定位

         对象创建如何在java堆中分配内存地址呢?两种方法:使用的内存在一边,空闲的在另一侧,中间用用指针作为分界点,分配时向空闲侧移动指针即可,此法成为指针碰撞。在使用Serial、ParNew等待有Compact过程的收集器时,系统使用此法分配对象内存。还有一种就是使用空闲列表记录哪些用了,哪些没用,像使用CMS这种基于Mark-Sweep收集器时,一般采用空闲列表。并发创建对象时如何同步操作分界指针或者空闲列表呢?两种方法:CAS+失败重试;本地线程分配缓存(Thread Local Allocation Buffer,TLAB),哪个线程要分配内存,就在自己的TLAB上分配,只有TLAB用完了要分配新的TLAB时才用同步锁定,虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定。
        对象存储时实际内存分三块:对象头,实例数据和对齐填充。
  • HotSpot虚拟机的对象头包括两部分信息,第一部分为对象自身的运行时数据,如hashCode、Gc分代年龄、锁状态标志、线程持有的锁、偏向线程id等;另外一部分是类型指针,指向类元数据的指针(并不是所有虚拟机都这么做的)。如果对象是数组那么对象头中还得有数组长度
  • 实例数据:类中定义的字段,无论从父类继承的还是子类定义的,各个字段的存储顺序和虚拟机分配策略有关以及java源码变量定义顺序有关。
  • 对齐填充:8字节对齐
         引用类型如何被访问java虚拟机规范并没有规定,目前主流访问方式有使用句柄和直接指针两种。
  • 句柄访问:需要在java堆中划一段内存来作为句柄池,引用存的就是对象的句柄地址,而句柄包括对象实例数据与类类型数据的具体地址信息

  • 直接指针访问:那么引用存的就是实例对象的地址,而类类型数据就得在实例对象中实现获取了。

        直接指针访问节省了一次指针定位,速度更快,而句柄访问的最大好处是引用中存的是稳定的句柄地址,对象被移动是只会改变句柄中的实例数据指针,而引用本身不变

3).变量存储内存区域总结

转自:java内存模型

这里所谓的堆和栈是jvm虚拟机中定义的,和 操作系统级别的进程的虚拟地址空间(内存四区)有所出入联系,不是同一概念

  • 一个本地变量可能是原始类型,在这种情况下,它总是“呆在”线程栈上。
  • 一个本地变量也可能是指向一个对象的一个引用。在这种情况下,引用(这个本地变量)存放在线程栈上,但是对象本身存放在堆上。
  • 一个对象可能包含方法,这些方法可能包含本地变量。这些本地变量任然存放在线程栈上,即使这些方法所属的对象存放在堆上。
  • 一个对象的成员变量随着这个对象自身存放在堆上。不管这个成员变量是原始类型还是引用类型。
  • 静态成员变量(引用)跟随着类定义一起也存放在方法区上。如果静态成员变量是引用类型new出来的,那么new出来的实际对象存储在堆上,只是这个静态引用是在方法区,这个跟本地变量有点相似。所以在类中声明一个静态成员变量并用new赋初值是完全可以的。单例模式就可以这么实现而不需要用双重检验锁。Java语言并不要求常量一定只有编译期预置入Class的常量表的内容才能进入方法区常量池,运行期间也可将新内容放入常量池(最典型的String.intern()方法)
  • 存放在堆上的对象可以被所有持有对这个对象引用的线程访问。当一个线程可以访问一个对象时,它也可以访问这个对象的成员变量。如果两个线程同时调用同一个对象上的同一个方法,它们将会都访问这个对象的成员变量,但是每一个线程都拥有这个本地变量的私有拷贝。


参考:
图片和内容大来自《深入理解java虚拟机》
http://www.cnblogs.com/paddix/p/5309550.html
http://blog.csdn.net/seu_calvin/article/details/51404589
http://www.cnblogs.com/lao-liang/p/5110710.html
http://blog.csdn.net/steady_pace/article/details/51254740
http://ifeve.com/under-the-hood-runtime-data-areas-javas-memory-model/
http://www.cnblogs.com/_popc/p/6096517.html
http://blog.csdn.net/u012152619/article/details/46968883
http://blog.csdn.net/ithomer/article/details/6252552
http://www.importnew.com/19612.html


原创粉丝点击