何时在实例创建期间分配内存或使用new关键字创建对象?

时间:2014-07-31 09:28:07

标签: java memory-management

当我在某处读到时,在创建实例并从堆分配空间时分配内存。如果它是正确的,那么在实例和对象创建期间分配了多少内存?

5 个答案:

答案 0 :(得分:7)

方法中声明的变量存储在堆栈中,而实际对象存储在堆中。考虑

Integer a = new Integer(10);

在此示例中,在堆上创建Integer类型的对象,并返回引用(32或64位)并将其作为变量' a'存储在方法堆栈中。如果JVM更喜欢优化,JVM可以自由地将这样的变量保存在CPU寄存器中。

使用new关键字时会分配对象的内存。它通常在TLAB(线程本地分配缓冲区)内分配,它是正在运行的线程保留的eden heap的一部分。从而将对象分配的开销减少到一个简单的指针碰撞'。不使用TLAB时的两次是1)当对象对于剩余的空间来说太大时,在这种情况下,当支持JVM决定通过{{3时,它将被直接提升到old gen和2)它可以完全避免对象并直接分配到堆栈上(甚至可以将对象分开并只分配堆栈中所需的字段)。

保留的内存量包括一个对象头,通常为2 escape analysis(数组为3),然后是对象及其父类中声明的每个字段的空间。这些字段的总大小取决于JVM和底层平台(例如32位或64位)和JVM配置(如压缩引用)。

------------------+------------------+------------------ +--------------------------
|   mark word     |   klass pointer  |  array size (opt) |    padding and fields   |
------------------+------------------+-------------------+--------------------------

要求JVM提供尺寸不受官方支持,但words是一个非常好的最佳猜测'它使用不同JVM的知识并通过EHCache sizeOf访问底层指针。

理解每个字段大小的起始位置是Java Unsafe,但这只是最小的大小,因为JVM设计为使用32位,因此小于此的基元通常被填充到32位。例如布尔人。

字段的确切布局将因JVM而异,但它们将倾向于按照从继承树的根开始定义它们的类进行分组。例如,考虑

enter image description here

enter image description here

上面的图片是从非常好的size of primitives defined by the Java language中拍摄的,它很好地描述了内存布局,

答案 1 :(得分:2)

"创建实例"与&#34具有相同的含义;使用new创建新对象"。

在正常情况下,堆内存将在您请求new对象时分配,但这不是一成不变的:HotSpot还可以确定在调用时分配对象是安全的堆栈(通过逃逸分析的过程)。这样更有效,因为它不需要任何垃圾收集。

分配了多少内存是高度特定于实现的,只保证Java数组被打包" (模数固定的开销)。但是,布尔数组被指定为每个元素占用一个字节。

答案 2 :(得分:1)

  

正如我在某处读到的那样,在创建实例并从堆中分配空间时分配内存。

是的,你是对的,除非new被调用,否则它只是一个指向任何内容的null引用。

  

如果它是正确的,那么在创建实例和对象的时候分配了多少内存?

取决于对象的大小。

请查看Primitive Data Types以了解他们的身材。

In Java, what is the best way to determine the size of an object?

Read more...

答案 3 :(得分:1)

我正在阅读你的问题:“谁为对象实际分配内存 - new关键字或构造函数?”如果是这种情况,答案是{{ 1}}关键字。

构造函数通常是链接的,这意味着在创建实例时至少会运行两个构造函数。另一方面,实例的内存仅分配一次。

此外,分配的类型是使用所产生的参考的使用分析来确定的(例如,逃逸分析)。这意味着发生分配的最明显的地方是构造函数调用站点(即new表达式的位置)。

分配的内存大小使得它可以容纳new关键字后面的类型实例。后一个大小(类型实例的大小)是

  • 原始类型的总大小(newintfloat等)由
  • 组成
  • 加上它包含的引用类型(类或接口实例)的引用的聚合大小,
  • 加上实例中嵌入的“秘密”内容,以便Java功能可以工作(如虚拟表指针,以便快速运行时解析虚方法调用)
  • 加上每个之间可能的填充(将各种类型对齐到内存中的最佳地址数倍)。

在任何情况下,当您执行double时,其中T obj1 = new T()是类的名称:

  1. 内存分配到某处以容纳T实例。
  2. 构建实例。
  3. 如果成功构造实例,则返回对实例的引用。
  4. 该引用存储在T变量中。
  5. 执行obj1时,R obj2 = new R()类型会发生类似情况,类型R的大小可能与R不同。

    但这些局部变量都不包含实例。它们都包含对其指定对象的引用。 (因此,变量本身甚至可能具有相同的大小,如果他们只需要存储一个内存地址。)

答案 4 :(得分:0)

类变量

当从同一个类蓝图创建多个对象时,它们每个都有自己不同的实例变量副本。在Bicycle类的情况下,实例变量是节奏,齿轮和速度。每个Bicycle对象都有自己的这些变量值,存储在不同的内存位置。

有时,您希望拥有所有对象共有的变量。这是通过静态修改器完成的。在声明中具有static修饰符的字段称为静态字段或类变量。它们与类相关联,而不是与任何对象相关联。该类的每个实例共享一个类变量,该变量位于内存中的一个固定位置。任何对象都可以更改类变量的值,但也可以在不创建类实例的情况下操作类变量。

https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html