一 :java虚拟机的基本结构
1: 类加载子系统(负责从文件系统或者网络中加载class信息,加载的类信息存放于一块成为方法区的内存空间。除了类信息外,方法区中可能还存放运行时常量池信息,包括字符串字面量和数字常量(这部分常量信息是class文件常量池部分的内存映射))
2:java栈(java方法的调用,保存着局部变量,方法参数,同时和java方法的调用、返回密切相关)
3:方法区(所有线程共享的内存区域,保存系统的类信息,比如类的字段、方法、常量池等)
4:java堆(在虚拟机启动的时候建立,它是java程序最主要的内存工作区域。几乎所有的java对象实例都存放于java堆中。堆空间是所有线程共享的,这是一块与java应用密切相关的内存区域)
5:直接内存(java的nio库允许java程序使用直接内存。直接内存是在java堆外的、直接向系统申请的内存区间。通常,访问直接内存的速度会优于java堆。因此,出于对性能的考虑,读写频繁的场合可能会考虑使用直接内存,由于直接内存在java堆外,因此直接内存的大小不会直接受限于xmx指定的最大堆大小,但是系统内存是有限的,java堆和直接内存的总和依然受限于操作系统能给出的最大内存)
6:本地方法栈(本地方法的调用,这是java虚拟机的重要扩展,java虚拟机允许java直接调用本地方法(这个方法通常使用c编写))
7:垃圾回收系统 (对方法区、java堆和直接内存进行回收。其中java堆是垃圾回收的重点。和c/c++ 不同,java中所有的对象空间释放是隐式的,也就是说对象的释放,开发人员不需要人为干预,对于不再使用的对象,垃圾回收系统会在后台自动查找、标识并释放垃圾对象,完成对java堆,方法区和直接内存中的全自动化管理)
8:pc寄存器(每个线程的私有空间,jvm会为每个java线程创建pc寄存器)
9:执行引擎(负责执行虚拟机的字节码)
二 : 初识java堆
根据垃圾回收机制的不同,java堆有可能拥有不同的结构。最为常见的一种构成是将整个java堆分为新生代(存放年龄不大的对象)和老年代(老年对象) 。简单说下java堆、方法区、java栈之间的关系
public class SimpleHeap{
private int id;
public SimpleHeap{
this.id=id;
}
public void show(){
System.out.println(id);
}
public static void main(String[] args){
SimpleHeap s1= new SimpleHeap(1);
SimpleHeap s2= new SimpleHeap(2);
s1.show();
s2.show();
}
}
请看下面图片解释
三 : java栈
如果说java堆和程序数据密切相关的话,那java栈就是和线程执行密切相关了,线程执行的基本行为就是函数调用每次函数调用的数据都是java栈传递的。
java栈与数据结构上的栈有着类似的含义,它是一个先进后出的数据结构,只支持出栈和入栈。java栈中主要保存的是栈帧,每一个栈帧里面又保存着局部变量表,操作数栈和栈数据区。记住:一个函数(java方法)就是一个栈帧
局部变量表:保存函数(java方法)的参数以及局部变量。局部变量表中的变量只在当前函数调用中有效,当函数调用结束后,随着函数栈帧的销毁,局部变量表也会随之销毁,由于局部变量表在栈帧之中,如果函数的参数和局部变量较多,会使得局部变量表膨胀,从而每一次函数调用就会占用更多的栈空间。在每个栈帧的的变量,大小都是用“”字“”来描述,字指的是计算机内存中占据一个单独的内存单元编号的一组二进制串,一般32位计算机上一个字为4个字节长度,
切记:栈帧中的局部变量表中的槽位是可以重复利用的,如果一个局部变量过了其作用域,那么在其作用域之后申明的心的局部变量就很有可能会复用过期局部变量的槽位,从而达到节省资源的母的。(不懂? 看下面代码)
public void localvarGc1(){
byte a = new byte[6*1024*1024];
System.gc();
}
public void localvarGc2(){
byte a = new byte[6*1024*1024];
a=null;
System.gc();
}
public void localvarGc3(){
{
byte a = new byte[6*1024*1024];
}
System.gc();
}
public void localvarGc4(){
{
byte a = new byte[6*1024*1024];
}
int c =10;
System.gc();
}
public void localvarGc5(){
localvarGc1();
System.gc();
}
对象User u 是类的成员变量,该字段有可能被任何线程访问,因此属于逃逸对象
下面展示一个非逃逸对象
总结:栈上分配依赖逃逸分析和标量替换的实现,关闭其中之一,栈上分配技术就达不到目的了。
对于大量的零散小对象,栈上分配提供了一种很好的对象分配优化策略,栈上分配速度快,并且可以有效避免垃圾回收带来的负面影响,但由于和堆空间相比,栈空间较小,因此对于大对象无法也不适合在栈上分配,
四:java虚拟机之 识别方法区
和java堆一样,方法区是一块所有线程都共享的内存区域。方法区用于保存系统的类信息,比如累的字段,方法,常量池等。方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内幕才能溢出错误。
在JDK1.6、JDK1.7中,方法区可以理解为永久区,默认情况下永久区的大小为64M。一个大的永久区可以保存更多的类信息。在JDK1.8中,永久区已经被彻底移除了。取而代之的事元数据区,这是一块堆外的直接内存。与永久区不同,如果不指定大小,默认情况下,虚拟机会耗尽所有的可用系统内存。
文章内容如有错误 欢迎加 QQ :82479297 探讨