JVM性能调优
JVM性能调优主要涉及内存管理、垃圾回收、线程管理和编译优化等方面,以下是一些常见的调优策略: 内存配置与调整: -Xms 和 -Xmx 参数用于设置JVM堆内存初始大小和最大大小,合理设置可避免频繁的内存扩容和缩容带来的性能损耗。 -Xmn 设定年轻代大小,调整新生代、老年代的比例有助于优化垃圾回收效果。 -XX:PermSize 和 -XX:MaxPermSize (在Java 8及以前版本中)或者 -XX:MetaspaceSize 和 -XX:MaxMetaspaceSize(Java 8及以后版本)控制元空间/永久代的大小。 垃圾回收调优: 选择合适的垃圾收集器,比如G1、ZGC、Shenandoah等,并根据应用特点调整其相关参数,如 -XX:+UseG1GC 等。 调整年轻代晋升阈值 -XX:NewRatio 和 -XX:SurvivorRatio,优化对象在各代间的分布。 对于并发标记扫描(CMS)垃圾收集器,可以调整 -XX:CMSInitiatingOccupancyFraction...
Java虚拟机(JVM)内存模型
Java虚拟机(JVM)内存模型,也称为Java内存模型(Java Memory Model, JMM),是Java程序在运行时数据存储和交互的抽象模型。它定义了线程如何共享和交互内存区域中的变量,以及如何确保并发环境下的内存一致性。 JVM内存主要分为以下几大区域: 堆内存 (Heap) 堆是所有线程共享的一块内存区域,主要用于存放对象实例。所有的对象都在堆中分配内存。 当一个对象不再被任何引用指向时,垃圾回收器会对其进行回收以释放内存。 根据对象生命周期的不同,堆内存通常又细分为新生代、老年代等区域,并采用不同的垃圾回收策略进行管理。 方法区 (Method Area/元空间 Metaspace) 方法区同样是一个各个线程共享的内存区域,存储已经被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 在Java 7及以前版本,方法区有时被称为“永久代”,但在Java 8及之后,HotSpot VM移除了永久代,将类元数据存放在本地内存的元空间中。 虚拟机栈 (Java...
Java虚拟机(JVM)的生命周期
Java虚拟机(JVM)的生命周期涵盖了从启动到退出的整个过程,包括以下几个主要阶段: 启动 (Startup) JVM的启动通常是由用户执行一个Java应用程序开始的,例如通过命令行输入java [类名]或调用API启动。 启动过程中,操作系统创建一个新的进程,并加载特定实现的JVM实例。引导类加载器(Bootstrap Class Loader)首先加载核心Java类库(如rt.jar),然后根据指定的主类(包含main()方法的类)找到并加载这个类。 JVM初始化内存区域和子系统,如堆、栈、方法区等。 类加载与初始化 (Class Loading and Initialization) 当JVM需要使用某个类时,类加载机制会启动,按照双亲委派模型加载对应的字节码文件(.class文件)到方法区中。 类的生命周期包括:加载(查找并读取类的二进制数据)、验证(确保类信息符合JVM规范且不危害虚拟机安全)、准备(为静态变量分配内存并初始化默认值)、解析(将符号引用转换为直接引用)、初始化(执行类初始化代码,即static块和静态字段赋值)。 程序执行...
字节码与执行引擎
Java虚拟机(JVM)字节码与执行引擎是Java平台实现“一次编写,到处运行(Write Once, Run...
类加载机制
JVM(Java虚拟机)内存加载流程主要指的是类加载机制,即Java类从磁盘上的.class文件或其他来源被加载到JVM内存中并成为可执行类的过程。以下是JVM类加载的详细步骤: 加载(Loading): 当程序首次主动使用某个类时(如调用new操作符创建实例、访问静态成员或调用类的静态方法等),JVM的类加载器子系统开始工作。 类加载器根据类的全限定名查找对应的.class文件或其他类定义资源。 将找到的.class文件字节流读入内存,并在内存的方法区内存空间内创建一个代表该类的Class对象。 在Java...
ArrayList中为什么子列表和原始列表会互相影响,如何避免这种影响
在 Java 中,ArrayList.subList()方法返回的子列表并不是一个独立的副本,而是一个视图(view),它指向原始列表中的某个范围。这意味着对子列表的操作实际上会直接反映到原始列表上,因为它们共享相同的底层数据结构。 例如,在给出的代码片段中: 1234List<Integer> integerList = new ArrayList<>();// ... 添加元素 ...List<Integer> subList = integerList.subList(0, 2);subList.set(0, 10); // 这将改变integerList的第一个元素 当调用subList.set(0, 10)时,虽然操作的是子列表,但实际上是更改了原始列表integerList在对应索引位置上的值。 为了避免这种影响,如果你需要对列表的部分内容进行修改而不影响原始列表,可以创建子列表的一个深拷贝(deep copy)而不是使用视图。例如,你可以使用ArrayList的构造函数来复制子列表: 12List<Integer>...
ArrayList扩容原理
Java 中ArrayList的扩容原理是在其内部数组无法容纳更多元素时,会创建一个新的更大的数组,并将原有数组中的所有元素复制到新数组中。具体过程如下: 初始容量与默认容量: ArrayList在初始化时不指定大小的情况下,默认容量为 10(即DEFAULT_CAPACITY = 10)。 当使用无参构造函数创建一个空ArrayList时,它会初始化一个空数组引用。 添加元素与自动扩容: 当通过add()方法添加元素,且当前存储元素的实际数量(size)等于当前数组的容量时,需要进行扩容操作。 扩容机制通常是将现有容量翻倍,也就是新的容量为原来的两倍加上一定的增长量(通常就是原容量)。扩容计算公式可以是:newCapacity = oldCapacity + (oldCapacity >> 1) 或者 newCapacity = oldCapacity * 1.5 + 1(取决于不同的 Java...
HashMap,TreeMap,LinkedHashMap的区别
Java 中的HashMap, TreeMap, 和 LinkedHashMap 都是实现 java.util.Map 接口的不同类,它们的主要区别在于数据存储结构、排序特性以及迭代顺序。以下是每个类的特点和使用场景: HashMap: 实现原理:基于哈希表(数组+链表或数组+链表+红黑树),通过 key 的 hashCode 值进行散列,并且当发生哈希冲突时采用链表或红黑树来解决冲突。 特点: 无序性:插入元素时没有特定的顺序,遍历结果不保证有序。 性能:查找、添加和删除操作的时间复杂度在平均情况下为 O(1)。 键值唯一:不允许有重复的键,但值可以重复。 线程安全性:非线程安全,在并发环境下需使用 Collections.synchronizedMap() 或 ConcurrentHashMap 来确保线程安全。 TreeMap: 实现原理:基于红黑树(自平衡二叉查找树)实现,自动对键进行排序。 特点: 有序性:根据键的自然顺序(对于实现了 Comparable...
HashMap保证线程安全的方法
在Java中,HashMap类本身并不是线程安全的。这意味着如果多个线程同时读写一个HashMap实例,可能会导致数据不一致或死锁等问题。为了保证线程安全,有以下几种方法可以实现: 使用 java.util.concurrent.ConcurrentHashMap ConcurrentHashMap 是专门为并发环境设计的线程安全的哈希表。它通过分段锁(Segment)机制来实现更高的并发性能,允许多个线程在同一时间对不同部分的数据进行操作。 使用 Collections.synchronizedMap() 包装 可以使用 Collections.synchronizedMap() 方法将一个普通的 HashMap 封装成线程安全的 Map 对象。这样得到的包装对象,在其上的所有操作都会被同步,从而保证线程安全。但请注意,即使进行了同步,迭代时也必须手动加锁,否则可能遇到并发修改异常。 使用 synchronized 关键字同步方法 在自定义类中,可以为 put, get, remove 等方法添加 synchronized...
HashMap其他注意事项
HashMap如果使用对象最为key,要注意什么?(难度:★★ 频率:★) 重写hashCode和equals。 对象比较为什么重写hashCode和equals?(难度:★★ 频率:★) 重写equals方法时需要重写hashCode方法,主要是针对Map、Set等集合类型的使用;a: Map、Set等集合类型存放的对象必须是唯一的;b: 集合类判断两个对象是否相等,是先判断HashCode是否相等,如果HashCode返回TRUE,还要再判断equals返回值是否ture,只有两者都返回ture,才认为该两个对象是相等的。