1、程序运行中栈可能会出现两种错误
- StackOverFlowError: 若栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java
虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。 - OutOfMemoryError: 如果栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
2、JDK1.7和JDK1.8的区别
3、Java 虚拟机栈和本地方法栈的区别和联系
Java虚拟机栈和本地方法栈是Java虚拟机(JVM)为每个线程分配的内存区域,用于执行方法的调用和执行。
- 区别:
- Java虚拟机栈:它用于存储Java方法的局部变量、方法参数、返回值和部分方法调用信息。每个线程在运行时都会创建一个对应的Java虚拟机栈,栈的大小可以动态调整。Java虚拟机栈采用后进先出(LIFO)的数据结构,用于支持方法的调用和返回。
- 本地方法栈:它类似于Java虚拟机栈,但是用于执行本地方法(Native Method)的调用和执行。本地方法是使用其他语言编写的方法,如C或C++,并且通过Java Native Interface(JNI)与Java代码进行交互。本地方法栈也是每个线程独立的,用于支持本地方法的调用和返回。
- 联系:
- Java虚拟机栈和本地方法栈都是为了支持方法的调用和执行而存在的。
- 它们都是线程私有的,每个线程都有自己的Java虚拟机栈和本地方法栈。
- Java虚拟机栈和本地方法栈都会随着方法的调用和返回而动态地进行入栈和出栈操作。
总之,Java虚拟机栈和本地方法栈在功能和作用上有所不同,但都是为了支持方法的调用和执行而存在的,并在运行时为每个线程分配独立的内存区域。
4、Java堆空间
在Java虚拟机中,堆是一块用于存储对象实例的内存区域。以下是关于Java虚拟机中堆的位置、作用和分布的梳理:
位置:
- 堆位于Java虚拟机的内存区域中,是Java应用程序运行时的主要内存区域之一。
作用:
- 堆用于存储Java程序中的对象实例。所有通过关键字
new
创建的对象都会在堆上分配内存。 - 堆是Java中动态内存分配的地方,对象的创建和销毁都在堆上进行。
- 堆提供了自动的内存管理机制,通过垃圾回收器来自动回收不再使用的对象所占用的内存。
分布:
- 堆的大小可以通过Java虚拟机的参数进行调整,一般分配给堆的内存大小会根据应用程序的需求进行动态调整。
- 堆被划分为年轻代和老年代两个区域,以支持不同的垃圾回收算法。
- 年轻代:年轻代用于存放新创建的对象,它又被分为Eden空间和两个Survivor空间(通常是From和To)。大部分对象在创建后会首先被分配到Eden空间。
- 老年代:老年代用于存放经过多次垃圾回收仍然存活的对象。一般来说,老年代中的对象生命周期较长。
- 堆的分布和对象的移动可以根据垃圾回收器的算法而有所不同,比如,新生代的垃圾回收一般采用复制算法,而老年代的垃圾回收一般采用标记-清除算法或标记-压缩算法。
总之,Java虚拟机中的堆是用于存储对象实例的内存区域,具有动态分配和自动回收的特性。它被分为年轻代和老年代,为Java应用程序提供了高效的内存管理机制。
5、方法区
- 当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。
- 方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
6、字符串常量池
7、JVM—永久代
- 在旧版的Java虚拟机中,存在一个称为"永久代"的内存区域。它位于堆内存之外,用于存储一些类的元数据信息,如类的结构、字段、方法、常量池等。永久代的主要功能是存储长时间存在的类信息,这些信息在运行时不会被回收。
- 然而,永久代存在一些问题。首先,永久代的大小是固定的,无法根据实际需要进行调整,容易导致内存溢出。其次,永久代的垃圾回收机制比较复杂,容易导致性能问题。此外,一些特殊的应用场景下,如动态生成大量类的情况,也容易导致永久代溢出。
- 因此,在Java 8版本中,JVM引入了元空间(Metaspace)来取代永久代。元空间是位于堆内存之外的内存区域,用于存储类的元数据信息。与永久代不同,元空间的大小可以根据实际需要进行动态调整,避免了永久代的内存溢出问题。此外,元空间的垃圾回收机制也更加简单高效。
- 总结起来,元空间取代永久代的原因主要是为了解决永久代存在的内存溢出和性能问题,并提供更好的灵活性和可靠性。
8、深入学习链接
9、Java的垃圾回收机制
- 当Java程序中的对象不再被引用时,垃圾回收机制会自动回收这些对象占用的内存空间,以便为新的对象腾出空间。
- Java的垃圾回收机制是自动化的,程序员不需要显式地释放内存。
Java的垃圾回收机制主要基于以下两个原则:
引用计数:每个对象都有一个引用计数器,当有引用指向对象时,计数器加1;当引用停止指向对象时,计数器减1。当计数器为0时,对象被认为是不可达的,可以被回收。
可达性分析:通过一系列称为"GC Roots"的根对象作为起点,通过对象之间的引用链,判断对象是否可达。如果对象不可达,则可以被回收。
Java的垃圾回收机制采用分代收集算法,将内存分为
- 新生代(Young Generation)存放新创建的对象
- 老年代(Old Generation)存放存活时间较长的对象
- 永久代(PermGen/Metaspace)存放类的元数据信息。
垃圾回收过程主要包括以下几个步骤:
标记:从GC Roots开始,对所有可达对象进行标记。
清除:清除所有未被标记的对象,释放其占用的内存空间。
压缩(可选):将存活的对象往一端移动,整理内存空间,以便为新的对象分配连续的内存空间。
需要注意的是,Java垃圾回收机制是与具体的JVM实现相关的,不同的JVM可能会有不同的垃圾回收算法和策略。一般来说,JVM会根据当前的内存使用情况和系统负载等因素,动态调整垃圾回收的频率和策略,以达到最佳的性能和内存利用率。