0
点赞
收藏
分享

微信扫一扫

JVM系列(八) -运行期的几种优化技术

月半小夜曲_ 2024-11-03 阅读 27
jvm

一、基本类型和包装类型的区别?

  • 用途:除了定义一些常量和局部变量之外,我们在其他地方比如方法参数、对象属性中很少会使用基本类型来定义变量。并且,包装类型可用于泛型,而基本类型不可以。
  • 存储方式:基本数据类型的局部变量存放在 Java 虚拟机栈中的局部变量表中,基本数据类型的成员变量(未被 static 修饰 )存放在 Java 虚拟机的堆中。包装类型属于对象类型,我们知道几乎所有对象实例都存在于堆中。
  • 占用空间:相比于包装类型(对象类型), 基本数据类型占用的空间往往非常小。
  • 默认值:成员变量包装类型不赋值就是 null ,而基本类型有默认值且不是 null
  • 比较方式:对于基本数据类型来说,== 比较的是值。对于包装数据类型来说,== 比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用 equals() 方法。

为什么说是几乎所有对象实例都存在于堆中呢? 这是因为 HotSpot 虚拟机引入了 JIT 优化之后,会对对象进行逃逸分析,如果发现某一个对象并没有逃逸到方法外部,那么就可能通过标量替换来实现栈上分配,而避免堆上分配内存

⚠️ 注意:基本数据类型存放在栈中是一个常见的误区! 基本数据类型的存储位置取决于它们的作用域和声明方式。如果它们是局部变量,那么它们会存放在栈中;如果它们是成员变量,那么它们会存放在堆/方法区/元空间中。

二、超过 long 整型的数据应该如何表示?

基本数值类型都有一个表达范围,如果超过这个范围就会有数值溢出的风险。

在 Java 中,64 位 long 整型是最大的整数类型。

long l = Long.MAX_VALUE;
System.out.println(l + 1); // -9223372036854775808
System.out.println(l + 1 == Long.MIN_VALUE); // true

BigInteger 内部使用 int[] 数组来存储任意大小的整形数据。

相对于常规整数类型的运算来说,BigInteger 运算的效率会相对较低。

三、Java内存区域

在 Java 中,内存区域主要分为以下几个部分:

一、程序计数器

程序计数器是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。每个线程都有一个独立的程序计数器,它是线程私有的。

作用:

  • 用于记录正在执行的虚拟机字节码指令的地址。
  • 如果正在执行的是本地方法,则程序计数器的值为空(Undefined)。

二、Java 虚拟机栈

Java 虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是 Java 方法执行的内存模型。

作用:

  • 每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
  • 每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

可能出现的异常:

  • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常。
  • 如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError 异常。

三、本地方法栈

本地方法栈与虚拟机栈所发挥的作用非常相似,区别是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的本地(Native)方法服务。

可能出现的异常情况与虚拟机栈相同。

四、Java 堆

Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。

特点:

  • 从内存回收的角度看,由于现在收集器基本都采用分代收集算法,所以 Java 堆中还可以细分为:新生代和老年代;再细致一点的有 Eden 空间、From Survivor 空间、To Survivor 空间等。
  • 从内存分配的角度看,线程共享的 Java 堆中可能划分出多个线程私有的分配缓冲区(Thread Local Allocation Buffer,TLAB)。

可能出现的异常:

  • 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出 OutOfMemoryError 异常。

五、方法区

方法区与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

特点:

  • 对方法区的回收主要是对常量池的回收和对类型的卸载。
  • 在 JDK 1.8 及之后,使用元空间替代了永久代,元空间使用的是本地内存,而不是 JVM 内存,这样可以避免永久代的内存溢出问题。

可能出现的异常:

  • 当方法区无法满足内存分配需求时,将抛出 OutOfMemoryError 异常。

六、运行时常量池

运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表,用于存放编译期生成的各种字面量和符号引用,这部分内容在类加载后进入方法区的运行时常量池中存放。

特点:

  • 运行时常量池相对于 Class 文件常量池的另外一个重要特征是具备动态性,Java 语言并不要求常量一定只有编译期才能产生,运行期间也可能将新的常量放入池中,这种特性被开发人员利用得比较多的便是 String 类的 intern () 方法。
举报

相关推荐

0 条评论