粗谈Java虚拟机是如何创建一个类的实例的?

本篇将简单探讨一下,JVM是如何一步一步创建一个对象的。

1. Class Loading

在一个特定的类能被实例化之前,JVM要做的第一步是需要将描述这个类的文件也就是.class文件给加载到内存里。
一般的做法是根据指定的classpath在文件系统里搜索.class文件。这个处理过程被称为class loading。
熟知的朋友们知道这里还有别的class loading方法,比如通过URL走网络加载。这里不细谈。总之第一步就是需要将对于特定类的class byte code给加载到内存。通过class loading这一过程,JVM会为这个类创建java.lang.Class实例,并进行静态初始化

2. 创建类实例

JVM创建一个类的实例的第二步,是为这个类的实例分配内存。

JVM所占用的系统内存,简单划分成了两块。

  1. 堆内存(Heap Memory):用于JVM虚拟机分配内存给类实例。
  2. 栈内存(Stack Memory):通常用于保存线程执行栈(thread stacks)信息和Java methods等。比较典型的例子是当我们递归调用一个方法过多层,就会导致java.lang.StackOverflowError了,每一次方法的调用(进入)都会占用一点堆内存,如果方法不退出堆内存讲得不到释放就会StackOverflow。

这下我们知道了大致的JVM的内存构成(堆内存还会继续分,详情见 Understanding Memory Management)。
也知道了类实例是分配在堆内存上。接下来我们来详细看一下JVM如何实例化一个对象的。也就是当我们写下 new myClass()的时候,究竟发生了什么?

根据JVM Specification Java SE8的例子

Creating a new class instance is a multistep process. The statement:

new myClass(i, j, k);

can be implemented by the following:

new #1 // Allocate uninitialized space for myClass
dup // Duplicate object on the operand stack
iload_1 // Push i
iload_2 // Push j
iload_3 // Push k
invokespecial #5 // Invoke myClass.

简单理解的话,是实例化一个对象需要经过几个步骤。大致区分的化分别是内存分配初始化

  • 对于初始化,java类的每一个Constructor都是一个特殊方法,需要被invokespecial命令所调用。所以理解起来呢和调用普通的方法一样即可。也一样可以抛出异常。

At the level of the Java Virtual Machine, every constructor written in the Java
programming language (JLS §8.8) appears as an instance initialization method
that has the special name <init>.

Instance initialization methods may
be invoked only within the Java Virtual Machine by the invokespecial instruction
(§invokespecial), and they may be invoked only on uninitialized class instances

  • 对于内存分配,则是会在堆内存上分配类实例所有变量加起来大小的内存。其中类实例变量(instance variables)可以是原生类型(Primitive Types)也可以是引用类型(Reference Types)

Memory for a new instance of that class is allocated from the
garbage-collected heap, and the instance variables of the new
object are initialized to their default initial values (§2.3, §2.4)

3. Java中各个类型所占内存的大小

老生常谈的问题,各个类型需要占多大的内存大小。这里笔者列举一下。

Data Type Size 备注
byte 8-bit
short 16-bit
int 32-bit
long 64-bit
char 16-bit
float 32-bit
double 64-bit
boolean ≤8-bit In Oracle’s Java Virtual Machine implementation, boolean arrays in the Java programming language are encoded as Java Virtual Machine byte arrays, using 8 bits per boolean element.
reference 32/64 bit 和c系语言的指针一样,根据JVM架构来定,如果JVM是32位的那么reference占用32bit大小,如果JVM是64位的那么占用64位大小。
java.lang.Object 8/16 byte 作为java的基底类,每一个实例都32位下占用8byte,64位下占用16byte大小。
  • 在JVM Specification里,除了boolean的定义比较暧昧,没看到特定大小除了指明Oracle JVM 实现的时候呢,boolean数组使用byte数组来表现的之外其他类型大小都是能知道的。计算一个类实例大小的时候可以认为boolean占用8-bit内存。
  • 当大量创建Java对象的时候,一定记得计算Object的大小,32位系统下1MB内存只够创建125万个Object实例。

结语

在高级语言里,内存管理被隐藏起来,但了解这些基本知识,正确认识内存是如何运作的是写出可靠程序的基础。
也愿各位不会在以后的项目里看到从数据库Select 1000万条数据,用数组来处理的代码。

参考文献
1. Understanding Memory Management
2. The Java® Virtual Machine Specification Java SE 8 Edition

发布了24 篇原创文章 · 获赞 24 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ToraNe/article/details/90350035