Java对象创建、分配、布局、访问小析(HotSpot虚拟机)(一)

简介: 本文内容总结自周志明先生所编著的《深入理解Java虚拟机-JVM高级特性与最佳实践》此书的经典不必多说。本节内容是对象的创建.、分配的内容。 对象的创建 java对象的创建有几种方式呢(这里所说的java对象仅限于普通java对象不包含数据和Class对象)?大致有以下四种方式: new关键字。这应该是我们最常见和最常用最简单的创建对象的方式。使用newInstance方法

本文内容总结自周志明先生所编著的《深入理解Java虚拟机-JVM高级特性与最佳实践》此书的经典不必多说。本节内容是对象的创建.、分配的内容。

对象的创建

java对象的创建有几种方式呢(这里所说的java对象仅限于普通java对象不包含数据和Class对象)?大致有以下四种方式:
  1. new关键字。这应该是我们最常见和最常用最简单的创建对象的方式。
  2. 使用newInstance方法。这里包括Class的newInstance方法和Constructor的newInstance方法(Class的newInstance方法最终调用的也是Constructor的newInstance方法)。
  3. 使用clone方法。要使用clone方法我们必须实现实现Cloneable接口,用clone方法创建对象并不会调用任何构造函数。即我们所说的浅copy
  4. 反序列化。要实现反序列化我们需要让我们的类实现Serializable接口。当我们序列化和反序列化一个对象,JVM会给我们创建一个单独的对象,在反序列化时,JVM创建对象并不会调用任何构造函数。即我们所说的深Copy
上面的四种创建对象的方法除了第一种使用new指令之外,其他三种都是使用invokespecial(构造函数的直接调用)。这里我们只说new创建对象的方式,关于invokespecial心急的同学可以看一下 http://wensiqun.iteye.com/blog/1125503。下面我们来看看当虚拟机遇到new指令的时候发生了什么事。
java虚拟机规范中规定了几种类初始化的几种条件,其中就有遇到new指令的时候(在虚拟机的生命周期中,一个类只会在一个类加载器初始化一次)。所以当虚拟机遇到一条new指令的时候首先会检查这个类有没有被初始化过,如果没有则首先进行类的初始化的操作。这个检查是怎么进行的呢?虚拟机会去检查这个指令的参数是否能在常量池中定位到一个相应的符号引用(关于符号引用的内容可以看一下R大的回答 https://www.zhihu.com/question/30300585?sort=created ) ,然后检查这个符号引用代表的类是否已被加载、解析、和初始化过。

分配内存

当上面的检查通过之后,虚拟机就会为新生对象分配内存了。这里需要注意的是: 对象所需内存的大小在类加载完成后便可以完全确定了。既然对象所需的内存大小可以确定了,那为对象分配内存空间就相当于从java堆中找一份相应大小的内存空间了。由于不同的虚拟机所采用的垃圾收集算法不同,或者相同的虚拟机根据不同的配置所采用不同的垃圾收集算法,所以会导致jvm中的内存可能是规整(有一块连续的内存空间)或者不规整(内存一个碎块一个碎块的)(使用Serial、ParNew等带Compact过程的收集器时,java内存是相对规整的,使用CMS这种基于Mark-Sweep算法的收集器时,java内存是相对不规整的。这一部分的内容参考垃圾收集器)。对于内存规整的,对象内存分配方式为“指针碰撞”,对于不规整的内存,对象内存分配方式为空闲列表方法。

指针碰撞

由于java堆中的内存是绝对规整(具体的参看标记压缩的垃圾回收机制)的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配的内存就仅仅是把那个指针指向空闲空间那边,然后挪动一段与对象大小相等的距离。

空闲列表

由于java堆中的内存是不规整的(具体的参看标记清除的垃圾回收机制)的,正在使用的内存和空闲的内存是交织在一起的,这个时候虚拟机会维护一个列表,在这个列表中会记录那些内存块是可以使用的,所在在分配内存的时候,只需要从列表中找到一块足够大的空间划分给对象就行了。
上面说的是两种为对象分配内存的方式,你以为有这两种内存分配方式就行了吗?图样图森破。只要做过项目的人都知道,对象的创建时多么频繁的一件事,所以这么频繁的创建对象就会产生线程安全的问题。有可能我现在正在给A对象分配内存,指针还没来得及修改,对象B又同时使用了原来的指针来进行内存分配。所以这个时候就需要考虑线程安全的问题了。jvm解决这个问题有两种方式,一种方式是使用CAS算法,另一种是使用 TLAB(Thread Local Allocation Buffer 本地线程分配缓冲)。即,把内存的分配动作按照线程划分在不同的空间之中进行,也就是每个线程在Java堆中预先分配一小块内存。哪个线程需要分配内存,就在哪个线程的TLAB上分配。

在内存分配完成之后,需要做的一件事是为属性赋初始值。然后虚拟机会对对象进行一些必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息,这些信息就存放在对象头中。对象头就是我们下节所要讨论的内容。

相关文章
|
24天前
|
存储 Java
Java中判断一个对象是否是空内容
在 Java 中,不同类型的对象其“空内容”的定义和判断方式各异。对于基本数据类型的包装类,空指对象引用为 null;字符串的空包括 null、长度为 0 或仅含空白字符,可通过 length() 和 trim() 判断;集合类通过 isEmpty() 方法检查是否无元素;数组的空则指引用为 null 或长度为 0。
|
2月前
|
Java
Java快速入门之类、对象、方法
本文简要介绍了Java快速入门中的类、对象和方法。首先,解释了类和对象的概念,类是对象的抽象,对象是类的具体实例。接着,阐述了类的定义和组成,包括属性和行为,并展示了如何创建和使用对象。然后,讨论了成员变量与局部变量的区别,强调了封装的重要性,通过`private`关键字隐藏数据并提供`get/set`方法访问。最后,介绍了构造方法的定义和重载,以及标准类的制作规范,帮助初学者理解如何构建完整的Java类。
|
2月前
|
安全 Java
Object取值转java对象
通过本文的介绍,我们了解了几种将 `Object`类型转换为Java对象的方法,包括强制类型转换、使用 `instanceof`检查类型和泛型方法等。此外,还探讨了在集合、反射和序列化等常见场景中的应用。掌握这些方法和技巧,有助于编写更健壮和类型安全的Java代码。
53 17
|
2月前
|
Java
java代码优化:判断内聚到实体对象中和构造上下文对象传递参数
通过两个常见的java后端实例场景探讨代码优化,代码不是优化出来的,而是设计出来的,我们永远不可能有专门的时间去做代码优化,优化和设计在平时
38 15
|
3月前
|
存储 监控 算法
深入探索Java虚拟机(JVM)的内存管理机制
本文旨在为读者提供对Java虚拟机(JVM)内存管理机制的深入理解。通过详细解析JVM的内存结构、垃圾回收算法以及性能优化策略,本文不仅揭示了Java程序高效运行背后的原理,还为开发者提供了优化应用程序性能的实用技巧。不同于常规摘要仅概述文章大意,本文摘要将简要介绍JVM内存管理的关键点,为读者提供一个清晰的学习路线图。
|
4月前
|
安全 Java 编译器
Java对象一定分配在堆上吗?
本文探讨了Java对象的内存分配问题,重点介绍了JVM的逃逸分析技术及其优化策略。逃逸分析能判断对象是否会在作用域外被访问,从而决定对象是否需要分配到堆上。文章详细讲解了栈上分配、标量替换和同步消除三种优化策略,并通过示例代码说明了这些技术的应用场景。
Java对象一定分配在堆上吗?
|
5月前
|
Java API
Java 对象释放与 finalize 方法
关于 Java 对象释放的疑惑解答,以及 finalize 方法的相关知识。
90 17
|
4月前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
5月前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
5月前
|
存储 Java 数据管理
Java零基础-Java对象详解
【10月更文挑战第7天】Java零基础教学篇,手把手实践教学!
52 6

热门文章

最新文章