一、内存分配区域
Java内存主要分为栈区、堆区、方法区等区域。
1.栈区:
栈区是Java内存管理中的一个关键区域,它主要用于存储局部变量和方法的执行环境(也称为栈帧)。每个线程在JVM中都有一个私有的栈,用于存储该线程中方法调用的上下文信息。栈区内存主要存放的数据有:局部变量表、操作数栈和动态链接。
局部变量表:包含了方法执行过程中所需的所有局部变量,包括基本数据类型的变量、对象引用等。
操作数栈:用于存放方法执行过程中产生的中间结果,以及计算过程中需要的操作数。它是一个后入先出的栈。
动态链接:每个栈帧内部都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。
2.堆区:
堆区是Java用来存储对象实例和数组的内存区域,它是JVM管理的最大一块内存区域,也是垃圾收集器管理的主要区域。堆区主要存放的数据有:对象实例、数组。
对象实例:通过new关键字创建的任何对象实例都存储在堆内存中。这些对象可以是用户自定义的类实例,也可以是Java库中的类实例。
数组:无论是基本数据类型的数组还是对象类型的数组,都存储在堆内存中。
其中,字符串常量池在JDK1.6的时候位于方法区,1.8之后位于堆区中。而在堆区的介绍中没有明确的列出字符串常量池是因为在现代Java虚拟机的实现中,字符串常量池虽然与堆区有紧密的关系,但它并不直接等同于堆区的一个独立子区域,而是有其特殊的存储和管理方式。实际上,字符串常量池中的字符串对象在物理上存储在堆区中,但JVM通过特定的机制(如字符串表)来管理和维护这些字符串对象,以实现字符串的共享和重用。堆区主要是用于存储对象实例和数组的内存区域,它关注的是对象的分配、回收等内存管理问题。而字符串常量池虽然与堆区有交集(即字符串对象存储在堆中),但它更多地是作为一个特殊的优化手段存在,用于提升字符串处理的性能和减少内存消耗。但1.8之后字符串常量池也是位于堆区中的。
3.方法区:
方法区是JVM中用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。它是所有线程共享的内存区域。方法区主要存放的数据有:类的结构信息、字段和方法数据、JIT编译器编译后的代码。
类的结构信息:包括类的名称、父类的名称、实现的接口、字段描述、方法描述等。
字段和方法数据:类的字段(包括静态变量和实例变量,但仅字段的描述信息存储在方法区,而字段的值存储在其他区域,如静态变量存储在方法区的静态变量区,实例变量存储在堆区)和方法的字节码内容、一些特殊方法(如构造函数、接口方法、虚方法、默认方法等)也存储在方法区。
JIT编译器编译后的代码:如果JVM开启了即时编译(JIT),那么被JIT编译后的本地机器码也会存储在方法区的某个部分。
需要注意的是,静态变量虽然逻辑上属于类,但它们的值(如果是对象或数组)的存储位置在堆上,而静态变量本身(即变量名和类型信息)的描述则存储在方法区。此外,实例变量和局部变量(基本类型或对象引用)的值也存储在堆上(实例变量)或栈上(局部变量),但它们的描述信息(即变量名和类型信息)则与类的结构信息一起存储在方法区。
二、Java中多态的内存
在Java中,多态是面向对象编程的一个重要特性,它允许子类对象被当作父类对象来使用。多态的实现涉及到方法的重写和动态绑定(详见静态绑定和动态绑定)。理解多态的内存模型有助于更好地掌握Java的内部工作机制。
在Java中,对象的内存分配和方法调用涉及到以下几个概念:
- 对象实例:实际创建的对象,存储在堆内存中。
- 引用变量:指向对象实例的引用,存储在栈内存中。
实际上这里可以运用动态绑定那一章的代码来解释了,直接复制过来,(加了一丢丢代码~~~)----------------------------源文本在这里->静态绑定和动态绑定
package oneHundredDayPlan.fourDay;
//动态绑定代码示例
class Animal {
public void dynamicBinding() {
System.out.println("dynamic binding");
}
}
class Dog extends Animal {
@Override
public void dynamicBinding() {
System.out.println("dynamic");
}
}
class Cat extends Animal {
@Override
public void dynamicBinding() {
System.out.println("binding");
}
}
public class Two {
public static void main(String[] args) {
Animal a = new Dog();
a.dynamicBinding();//dynamic
Animal b = new Cat();
b.dynamicBinding();//binding
Dog dog = new Dog();
dog.dynamicBinding();//dynamic
Cat cat = new Cat();
cat.dynamicBinding();//binding
}
}
代码详解:
这段代码中,
第一个new Dog();
在堆区中创建了一个Dog对象
第一个new Cat();
在堆区中创建了一个Cat对象
Dog dog = new Dog();
在堆区中创建了一个Dog对象
Cat cat = new Cat();
在堆区中创建了一个Cat对象
(是两个Dog对象和两个Cat对象,是不同的对象)
Animal a是一个animal类型的引用,存储在栈内存中,指向堆区中的Dog对象
Animal b是一个animal类型的引用,存储在栈内存中,指向堆区中的Cat对象
Dog dog是一个Dog类型的引用,存储在栈内存中,指向堆区中的Dog对象
Cat cat是一个Cat类型的引用,存储在栈内存中,指向堆区中的Cat对象
当调用a.dynamicBinding()方法时,JVM 会检查a引用实际指向的对象类型(Dog),然后调用Dog类的dynamicBinding方法。
当调用b.dynamicBinding()方法时,JVM 会检查b引用实际指向的对象类型(Cat),然后调用Cat类的dynamicBinding方法。
当调用dog.dynamicBinding();时,JVM 会检查dog引用实际指向的对象类型(Dog),然后调用Dog类的dynamicBinding方法。
当调用cat.dynamicBinding();时,JVM 会检查cat引用实际指向的对象类型(Cat),然后调用Cat类的dynamicBinding方法。
标签:存储,Java,对象,堆区,多态,Dog,内存,Cat From: https://blog.csdn.net/2402_82356599/article/details/142366812