进程 :进程是程序的一次执行过程。系统运行一个程序就是进程从创建到运行再到消亡的过程。
线程 :一个进程中包含多个线程,线程共享进程的堆和方法区的资源以及直接内存,同时线程私有资源的包括 程序计数器 、 虚拟机栈 和 本地方法栈 。
虚拟机栈描述的是Java方法执行的内存模型,生命周期与线程相同。每个Java方法在执行时会创建一个栈帧用于存放局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
虚拟机栈中会出现StackOverFlowError和OutOfMemoryError两种异常。
和虚拟机栈类似,区别在于 虚拟机栈为执行Java方法服务,本地方法栈则为执行本地方法服务 。本地方法栈中也会出现StackOverFlowError和OutOfMemoryError两种异常。
堆是JVM所管理的内存中最大的一块,是线程共享的一块内存区域,在JVM启动时创建, 用于存放对象实例,几乎所有的对象实例以及数组都在堆上分配内存 。
堆是垃圾收集管理的主要区域,也被称为GC堆。从GC的角度,当前的垃圾收集器都基本采用分代垃圾收集算法,因此堆可以细分为:新生代、老年代、永久代(JDK1.8被移除,被直接内存中的元空间代替)。
新生代用于存放新生的对象,一般占据堆空间的1/3。新生代又细分为Eden、From Survivor以及To Survivor,三者占比为8:1:1。新生代中会频繁地进行Minor GC操作实现垃圾回收。
老年代主要存放应用中生命周期长的内存对象。老年代比较稳定,不会频繁地进行Full GC。在进行Full GC前会进行一次Minor GC,当新生对象进入老年代导致内存不够时Full GC才会触发。当无法找到足够大的连续空间分配给新创建的较大对象也会提前触发一次Full GC进行垃圾回收腾出空间。
老年代中Full GC采用标记-清除算法 ,Full GC的耗时比较长,因为要扫描再回收。Full GC会产生内存碎片,当老年代也没有内存分配给新来的对象的时候,就会抛出OutOfMemoryError异常。
永久代指的是永久保存区域,主要存放Class和Meta(元数据)的信息。在JDK1.8中,永久代被元空间代替,元空间不在虚拟机中,而是使用直接内存。
采用元空间不使用永久代的原因:
方法区与对一样都是各个线程共享的内存区域,用于 存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据 。
JDK1.8中将方法区移除,取而代之的是元空间,位于直接内存中。
方法区是Java虚拟机规范中的定义,是一种规范。永久代是对方法区的一种实现,是HotSpot的概念。
运行时常量池是方法区的一部分,用于 存放编译期生成的各种字面量和符号引用 ,收到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。
JDK1.7及之后版本将运行时常量池从方法区中一处,在 堆 中开辟一块区域存放运行时常量池。
直接内存不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁使用,也可能导致抛出OutOfMemoryError异常。
JDK1.4中新加入的NIO(同步非阻塞IO),引入了一种基于通道(Channel)和缓存区(Buffer)的I/O方式,可以直接使用本地函数库分配对外内存,然后通过存储在堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,避免了在Java堆和本地堆之间来回复制数据。
原文链接:https://www.cnblogs.com/chiaki/p/13472146.html