外国网站架构,网站推广原则,东莞大型网站建设公司,做可视化的网站内存模型 内存模型如下图所示 堆
堆是Java虚拟机所管理的内存最大一块。堆是所有线程共享的一块内存区域#xff0c;在虚拟机启动时创建。此内存区域唯一的目的就是存放对象实例。所有的对象实例都在这里分配内存 Java堆是垃圾收集器管理的主要区域。从内存回收的角度来看在虚拟机启动时创建。此内存区域唯一的目的就是存放对象实例。所有的对象实例都在这里分配内存 Java堆是垃圾收集器管理的主要区域。从内存回收的角度来看由于现在的垃圾收集器采用的是分代收集算法。所以java堆又分为新生代和老年代。从内存分配的角度来说线程共享的java对中可能划分出多个线程私有的fenp缓冲区(Thread Local Allocation Buffer)。 可以通过 -Xms、-Xmx分别控制堆初始化是最小堆内存和最大堆内存大小。 虚拟机栈
与程序计数器一样java虚拟机栈也是线程私有的他的生命周期与线程相同。 虚拟机栈描述的是Java方法的执行的内存模型每个方法在执行的同时会创建一个栈桢stack frame用于存储局部变量表、操作数栈、动态链表、方法出口等信息。每个方法从调用直至执行完成的过程就对应着栈桢在虚拟机栈中入栈到出栈的过程。 虚拟机栈存储的数据类型
局部变量表 存放的是编译器可知得到各种基本数据类型boolean、byte、char、short、int、float、long、double、对象引用refrence类型不等同于对象本身一个指向对象的起始内存位置的引用指针
操作数栈
动态链表
方法出口
...
常见异常
在虚拟机规范中对这个区域规定了两种异常情况 如果线程请求的栈深度大于虚拟机所允许的深度将抛出StackOverflowError
如果虚拟机栈可以动态扩展扩展时无法申请做够的内存将会爬出OutOfMemorryError
本地方法栈
与虚拟机栈发挥的作用非常类似他们之间的区别是虚拟机栈为虚拟机执行java方法服务而本地方法栈则为虚拟机使用到的native方法服务。与虚拟机栈一样本地房发展区域也会抛出StackOverflowErrorOutOfMemorryError异常。 方法区1.8后该区域被废弃
方法区与java堆一样是各个线程所共享的它用来存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。 方法区是jvm提出的规范而永久代就是方法区的具体实现。 java虚拟机对方法区的限制非常宽松可以像堆一样不需要连续的内存可可选择的固定大小外还可以选择不识闲垃圾收集相对而言垃圾收集行为在这边区域是比较少出现的。 在方法区会报出 永久代内存溢出的错误。而java1.8为了解决这个问题就提出了meta space元空间的概念就是为了解决永久代内存溢出的情况一般来说在不指定 meta space大小的情况下虚拟机方法区内存大小就是宿主主机的内存大小 程序计数器
程序计数器是一块较小的内存空间他可以看做是当前线程所执行字节码的行号指示器。在虚拟机的概念模型里字节码解释器工作时就是通过改变这个计数器的值来选择下一条将要执行的字节码指令。 由于JAVA虚拟机的多线程是通过多线程流转切换并分配处理器执行时间的方式来实现的。在任一一个确定的时刻一个处理器都只会执行一条线程中的指令。因此为了线程切换后能恢复到正确的执行位置每条线程都需要一个独立的程序计数器各个线程的计数器之间互不影响独立存储我们称该类内存区域为线程私有 如果线程正在执行一个Java方法这个计数器记录的是正在执行的虚拟机字节码指令的地址。 运行时常量池
运行时常量池是方法区的一部分。Class文件除了 有类的版本、字段、方法、接口等描述信息外还有一项是常量池用于存放编译期生成的各种字面量和符号引用这部分内容在类加载后进入方法区的运行时常量池。 运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性.Java语言并不要求常量一定只有在编译器才能产生依旧是并非预置入Class文件中的常量池的内容才能进入方法区运行时常量池 对象创建
在语言层面上创建对象克隆反序列化通常只是一个new关键字。 过程
虚拟机在遇到一条new指令时首先去检查这个指令的参数是否能在常量池中定位到这个类的符号引用并且减产这个符号引用代表的类是否已经被加载、解析、和初始化国。如果没有那必须执行相应的类加载过程。 在类加载检查通过后接下来虚拟机将为新生的对象分配内存。对象所需内存的大小在类加载完成后便可完全确定。 内存分配
为对象分配空间的任务等同于把一块确定大小的内存从java堆中划分出来。假设java堆中内存是绝对规整的所有用过的内存都放在一边空闲的放在另外一边中间放着一个指针最为分界点的指示器那所分配内存就仅仅是把那个指针向空闲的空间那边挪动一段与对象大小相等的距离这种分配方式称为指针碰撞。 如果内存不是规整的已使用的和空闲的内存区域是相互交错的虚拟机必须维护一个列表记录哪些内存块是可用的在分配的时候从列表中找到一块足够大的空间划分给对象实例并更新这个列表。这种分配方式是空闲列表。 选择哪种分配分配方式是由java堆是否规整来决定的而java堆是否规整又是由其所采用 的垃圾收集器是否带有压缩规整的功能决定因此使用Serial、ParNew等带来Compat过程的收集器时分配算法是指针碰撞。而使用CMS这种基于Mark-Swaeep算法采用的是空闲列表分配方式。 对象的内存布局
在HotSpot虚拟机中对象在内存中的存储布局分为3块区域对象头Header、实例数据Instance Data、对其补充Padding 对象头Heading
对象头包括两部分信息 用于存储对象自身运行时数据。
如哈希码GC分代年龄、锁状态标志、偏向线程ID。这部分s数据的长度在32位和64位的虚拟机中分别为32bit和64bit。 对象的访问定位
创建对象时为了使用对象java程序需要通过栈上的refrence数据来操作堆上的具体对象。由于refrence类在java虚拟机值规定了一个指向对象的引用并没有定义这个引用应该通过何种方式去定位、访问堆中的具体位置所以对象访问方式也取决于虚拟机对这个refrence的具体实现目前主流的访问凡是是使用句柄、直接指针两种。 句柄访问
如果使用句柄访问的话那么java堆中就会划分出一块内存来座位句柄池refrence中存储的就是对象的句柄地址而句柄中包含了对象实例数据和类型数据的各自具体的地址信息。 指针直接访问
如果使用指针直接访问那么java堆对象的布局就必须考虑如何放置访问类型数据的相关信息而refrence中存储的直接就是对象地址 这个两种访问方式各有优势使用句柄访问的最大好处就是refrence中存储的是稳定的句柄地址在对象被移动垃圾收集时只会改变句柄中对象实例指针refrence本省不需要修改。 使用直接指针访问的方式最大的好处就是速度更快节省了一次指针定位的事件开销。由于对象的访问在java中非常频繁一次这类开销积少成多也是一个比较客观的优化。