JVM内存划分
首先有class文件=》由类加载器加载class文件到内存空间=》内存空间(方法去、堆、虚拟机栈、程序计数器、本地方法栈)=》垃圾回收负责对内存空间的清理工作=》
JVM有两种类加载器:启动类加载器(JVM实现的一部分)和用户自定义的类加载器(Java程序的一部分:ClassLoad的子类),有如下几种:
在jdk1.6及以下版本中,方法区还存放了常量池(例如字符串常量池),在jdk1.7及以后的版本中常量池被移到堆区了
- 堆:虚拟机启动时创建的所有线程共享的区域,主要用来存放对象的实例(通过new操作创建的对象实例),垃圾回收的重点管理区域;
- 虚拟机栈:栈是线程私有的区域,栈与线程具有相同的生命周期
栈主要用来实现Java方法的调用和执行,每个方法在执行时都会创建一个栈帧用来存储方法的局部变量、操作栈、动态链接和方法出口等信息;方法调用时通过入栈和出栈操作进行栈空间的分配和释放
- 程序计数器:线程私有的资源,JVM会为每个线程创建单独的程序计数器
它可以被看作是当前线程执行的字节码的行号指示器,解释器的工作原理便是通过改变这个计数器来确定下一条需要被执行的字节码指令,程序的流程控制(循环、分支、异常处理)都是通过它来完成的
当线程调用Java方法时,JVM会创建栈帧压入虚拟机栈,但当调用的是本地方法时,虚拟机栈保持不变,而是简单的动态链接并直接调用指定的本地方法,如果本地方法接口使用的是C++连接模型,那么它的本地方法栈就是C++栈
运行时内存划分
年轻代、老年代和永久代
根据对象的生命周期长短不同将其分为不同的类型(年轻代、老年代和永久代)并分别进行垃圾回收:分代垃圾回收
主要思路:把堆分为两个或多个子堆,每个子堆被视为一代,在运行中优先收集年幼对象,如果一个对象经过多次收集仍然存活那么就把它转移到高一级的堆里,以减少对其扫描的次数;目前最常用的JVM是SUN(现被Oracle收购)公司的HotSport所采用的算法是分代回收
- 年轻代Young Generation:分为3个部分(一个Eden区和两个相同的Survivor区=from和to区)Eden区主要用来存储新建的对象,在使用复制回收算法时作为双缓存起到内存整理的作用,Survivor区始终有一个是空的
- 老年代Old Generation:主要存储生命周超长的对象、超大(无法在年轻代分配)的对象;
- 永久代Permanent Generation:存放代码、字符串常量池、静态变量等可以持久化的数据;是一片连续的堆空间;基本不参与垃圾回收;SunJDK把方法区实现在了永久代。
新建的对象优先在Eden区分配内存,如果Eden已满则在创建对象时会因无法申请到空间而触发minorGC,minorGC主要用来对年轻代进行回收:把Eden区不能回收的对象放到空的Survivor区,另一个Survivor区里不能回收的也放到这里,这样就保证有一个Survivor区是空的;但是如果Survivor区也满了或者有些对象已经存在了很长时间,那么这些对象会被放到老年代中,老年代满触发fullGC。
fullGC是用来清理整个堆空间的,会造成很大的系统资源开销,应尽量避免,触发fullGC的情况:
- 调用System.gc()方法
- 老年代空间不足;根据实际情况增大Survivor区、老年代空间或调低触发并发GC(并发垃圾回收)的比率
- 永久代满;可以增大永久代空间(设置永久代大小:-XX:MaxPermSize=16m)、开启CMS回收永久代选项、应用程序线程并发的垃圾回收线程进行垃圾回收操作
Java8已移除了永久代,新加了一个称为元数据区的native内存区,大部分类的元数据都在本地内存中分配。
String.intern()
String s1 = new String("abc"); // 等价于"abc"和new String()两个操作,先从常量池取"abc"再作为参数传入构造方法
String s2 = "abc";
只要使用new总会生成新的对象;String的实现采用了Flyweight的设计模式优先使用常量池中存在的对象,s2时"abc"在编译时产生并放到常量池中,s1时在运行时把"abc"放到堆中;
元空间MetaSpace
jdk1.8开始永久代被移除,类的元数据被放到了本地化的堆内存(native heap)中即MetaSpace元空间;
移除永久代的原因:
- 永久代内存经常不够用或发生内存泄漏而抛出异常:Java Web开发需要动态生成类,而元空间有非常大的存储空间;
- 可以促进HotSpot JVM和JRockit VM的融合,JRockit没有永久代;
- 在HotSpot中每个垃圾回收器都需要专门的代码来处理存储在永久代中类的元数据信息,把类的信息转移到MetaSpace(与Java Heap有相同的地址空间)后可以实现MetaSpace与Java Heap的无缝化管理,简化FullGC的过程
MetaSpace的内存分配:
MetaSpaceVM分配内存:类与类加载器有相同的生命周期,只有类加载器还存活,MetaSpace中的类信息就不能释放,MetaSpaceVM通过一个块分配器来管理内存的分配,块的大小取决于类加载器的类型。VM维护一个全局的块列表,类加载器按需取块,当类加载器生命周期结束时释放块,把申请的块归还到全局块列表,每个块被分为多个block,每个block存储一个元数据单元;
由于类的大小不固定,当类加载器需要一个块时,有可能空闲的块太小而不足以容纳当前类,就会出现内存碎片,目前MetaSpaceVM还没有解决这个碎片问题。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。