对象:内存上来说是分配在堆上面的一块内存区域
类:把一类具体事物相同特征,功能/行为抽象为属性与方法过程。
类是对象的模板,对象是类的具体表现。
构造函数:与类名同名的函数,用来实例化对象并初始化成员变量。
类的结构:
静态属性与变量首先加载。其次时静态块,之后是代码块,最后是构造函数。
在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。【包括运行时常量池,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来,存储编译期间生成的字面量和符号引用。】
堆数据区是用来存放对象和数组(特殊的对象)。堆内存由多个线程共享。堆内存随着JVM启动而创建。如果堆内存剩余的内存不足以满足于对象创建,JVM会抛出OutOfMemoryError错误。【堆区中不存放基本类型和对象引用,只存放对象本身。】
Java栈也称作虚拟机栈(Java Vitual Machine Stack),Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,完成压栈和出栈操作。Java栈是Java方法执行的内存模型。【栈帧:一个栈帧随着一个方法的调用开始而创建,这个方法调用完成而销毁。在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。】
在JVM栈这个数据区可能会发生抛出两种错误:
1. StackOverflowError 出现在栈内存设置成固定值的时候,当程序执行需要的栈内存超过设定的固定值会抛出这个错误。
2. OutOfMemoryError 出现在栈内存设置成动态增长的时候,当JVM尝试申请的内存大小超过了其可用内存时会抛出这个错误。
当一个类被创建,并且这个类是首次被加载时(例如:A a=new A();),方法区会开辟出一块内存存放类的class文件并且将全部成员放入。之后会在堆中开辟一块内存,存储这个类并且将这个类的非静态的成员变量拷贝过来(静态成员不拷贝,所有实例共享),并持有对应的方法区的方法的句柄,这块内存有一个唯一内存地址,栈中的a对象指向的就是这个内存地址。
之后你为类的成员变量赋值时,堆中的变量的值会从默认值更改为设定值(方法区中变量无值)。
如果此时在实例化一个新的类(A a2=new A();),此时方法区中已经有一个A类的class,所以不会在创建一个A.class,但是此时会在堆中开辟一块新的空间并且将这个类的非静态成员拷贝并持有对应的方法区类的方法的句柄,这块内存空间标注一个新的内存地址。
此时,栈中a指向的是堆中第一个类的内存地址,a2指向的是堆中的第二个类的内存地址,而堆中这两块内存地址指向的是同一个方法区的class文件。
所以栈中对象要么存的是一个内存地址(引用)要么就是一个具体的值,存放的是一个具体值的话他就是一个基本变量。
对象实例化过程如下:
①加载类②为对象分配内存③对象属性的默认初始化④设置对象头⑤对象初始化(属性的显示初始化,代码块中的初始化,构造器中的初始化)
创建对象的方式:
(1)new 的方式
(2)Class的newInstance()方法
(3)Constructor的newInstance()方法
(4)clone()的方式
(5)反序列化的方式
如果进行了类加载(包括已加载和未加载需要加载的情况),那么最终结果都是生成一个Class类对象。
对象实例:Java中的一个类,由这个类的构造器 new
出来的对象就是对象实例。我们平常说的实例对象、对象实例,或者X类对象,都是同一个意思,都是指某个类实例化出来的对象。
Class类对象:Class类是一个实际存在的类,类名就叫Class(其含义不是我们常见的关键字class
)。这个类位于java.lang
包下,用于记录每一个类的类型信息。虚拟机会为每一个类(类或接口)都生成一个Class类对象,这个Class类对象就是Class这个类实例化出来的一个对象。每一个Class类对象都保存着虚拟机运行时,所对应的类的所有信息(比如类型,属性,方法,所在包名等等)。
Class类对象跟其他类对象一样都存放在堆区,但Class类的构造方法是私有的,所以我们在开发中不能显示的去调用并生成Class类对象,只有虚拟机才可以生成Class类的对象实例。Class类对象是在运行时提供某个对象的类型信息,故只在运行时才能通过Class类来获取对应的类的信息。一个类的所有实例对象共同拥有唯一的一个Class类对象。
首先计算对象占用空间的大小,接着就在堆中划分一块内存给新对象,如果实例成员变量是引用变量,仅分配引用变量空间即可(4个字节)。实际一个对象的大小在一步已经确定了。
内存是否规整:指针碰撞法/空闲列表法
处理并发安全问题:(堆空间是线程共享的)
在已经分配了的对象内存空间中,为对象的属性进行一个默认的初始化。即将对象的所有属性设为默认值(一般是零值),以保证对象实例字段在不赋值的情况下依然可以直接使用。
对象头分为两部分:Mark Word(运行时元数据)和类型指针。
①Mark Word:用于存储对象自身的运行时数据信息,如上述的哈希码、GC分代年龄、线程持有的锁等。
②类型指针:类型指针指向类的元数据,虚拟机通过这个指针确定该类在哪个变量中。
虚拟机执行init方法,以进行初始化成员变量,执行实例化代码块,调用类的构造方法,并把堆内对象的首地址赋值给引用变量。
执行完initi方法,把对象按照我们的意图进行初始化,这样一个真正可用的对象才算完全被创建出来。
< init >()方法中的代码顺序:
①静态变量②静态代码块③普通变量④普通代码块⑤构造器
父类和子类的初始化顺序:
①父类变量初始化②父类代码块③父类构造器④子类变量初始化⑤子类代码块⑥子类构造器
public class Customer {
int id = 1001;
String name;
Account acct;
{
name = "匿名客户";
}
public Customer(){
acct = new Account();
}
}
class Account{
}
public class CustomerTest {
public static void main(String[] args) {
Customer customer = new Customer();
}
}
参考:
java类实例化内存过程与面向对象特征_javarrr的博客-CSDN博客
【JVM第六篇--对象】对象的实例化、内存布局和访问定位 - 就行222 - 博客园
Java JVM 中 堆,栈,方法区 详解_张启露的博客-CSDN博客_java jvm 方法区