运行时数据区域:
     方法区+堆-> 由所有线程共享
    虚拟机栈+本地方法栈+程序计数器-> 线程私有
程序计数器:
    一块较小的内存空间,用于指标当前线程所执行的字节码行号。每个线程都需要一个独立的程序计数器,因此它是线程私有的。
    此区域是JVM规范中唯一不会出现OutOfMemoryError的区域。
JVM栈:
    线程私有,生命周期与线程相同,描述的是java方法执行的内存模型。通常所说的栈内存就是指虚拟机栈中的局部变量表,局部变量表所需的内存空间在编译期完全分配,是可确定的。
    此区域会出现StackOverflowError和OutOfMemoryError异常。
    StackOverflowError:线程请求的栈深度大于虚拟机栈允许的深度时出现。
    OutOfMemoryError:虚拟机栈动态扩展无法申请到足够的内存时出现。
本地方法栈:
    与JVM栈作用类似,因此也是线程私有的,只不过本地方法栈为虚拟机使用到的本地方法服务。在Hotspot虚拟机中把虚拟机栈和本地方法栈合二为一了。
    此区域也会出现StackOverflowError和OutOfMemoryError异常。
Java堆:
    JVM所管理的内存中最大的一块,被所有线程共享,由JVM启动时创建,用于存放对象的实例。但并不是所有的对象或数组在此堆上分配。该区域是GC的主要区域,由于现在的收集器基本采用分代收集算法,所以java堆通常分为新生代和年老代,新生代又分为Eden、From survivor、To survivor空间等。java堆不要求物理上内存连续,只要逻辑上连续即可。
    若堆中没有内存完成实例分配且堆无法再扩展时就会出现OutOfMemoryError异常。
方法区:
    由所有线程共享,用于存储虚拟机加载的类、常量、静态变量、即时编译器编译后的代码等数据。在Hotspot虚拟机中把这块叫永久代。
    此区域同java堆一样,不要求物理上内存连续,可以选择固定的大小和可扩展,最重要的是可以选择不实现垃圾回收。这个区域若要回收主要是回收常量池和卸载类。
    当方法区无法满足内存分配需求时会出现OutOfMemoryError异常。
    运行时常量池属于方法区的一部分,用于类加载后存放编译期生成的各种字面量和符号引用,运行时常量池具备动态性,即可以在运行期间将新的常量放入池中,如String的intern方法使用。此区域的大小自然会受到方法区大小的限制。
直接内存:
    不是虚拟机运行时数据区域的一部分,也不是JVM规范中定义的内存区域,如通过本地函数库直接访问的堆外内存就是直接内存。也可能会导致OutOfMemoryError异常。本机直接内存不会受到Java堆大小的限制,但会受到本机总内存大小及处理器寻址空间的限制。

    只要不断地创建对象,并保证GC Roots到对象之间有可达路径避免垃圾回收机制清除这些对象,就会在对象数量到达最大堆的容量限制后产生内存溢出异常。
   给每个线程的栈分配的内存越大,反而越容易产生内存溢出异常,因为操作系统给每个进程分配的内存是有限制的,每个线程栈容量越大,可以建立的线程数量自然就越少。
    要使运行时常量池发生溢出最简单地方法就是使用String的intern这个本地方法,不断地添加String的intern后的字符串就易把运行时常量池填满发生溢出。
   要使方法区发生溢出最简单地方法就是加载大量的类,如通过CGLib增强的类越多,则需要越大的方法区去载入Class。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐