Java内存管理(最新)
首页 专栏 java 文章详情
0

Java内存管理(最新)

Java攻城师 发布于 3 月 2 日

对Java内存管理的深入了解将增强您对堆的工作方式,引用类型和垃圾回收的了解。

您可能会认为,如果您使用Java进行编程,那么您需要了解有关内存工作原理的哪些信息?Java具有自动内存管理功能,这是一个不错的安静的垃圾回收器,它在后台工作以清理未使用的对象并释放一些内存。

因此,作为Java程序员,您不再需要像销毁对象那样麻烦自己,因为不再使用它们。但是,即使该过程在Java中是自动的,也不能保证任何事情。通过不知道垃圾收集器和Java内存的设计方式,即使您不再使用垃圾回收器和Java内存,您也可能拥有不符合垃圾收集条件的对象。

因此,了解内存在Java中的实际工作方式非常重要,因为它为您提供了编写高性能和优化应用程序的优势,这些应用程序永远不会因崩溃而崩溃OutOfMemoryError。另一方面,当您发现自己的状况很糟时,您将能够快速找到内存泄漏。

首先,让我们看一下内存通常是如何用Java组织的:

通常,内存分为两大部分:堆栈和堆。 请记住,此图片中的内存类型大小与实际内存大小不成比例。与堆栈相比,堆是大量的内存。

堆栈

堆栈内存负责保存对堆对象的引用,并负责存储值类型(在Java中也称为原始类型),这些值类型本身保存值,而不是对堆中对象的引用。

另外,堆栈上的变量具有一定的可见性,也称为scope。仅使用活动范围中的对象。例如,假设我们没有任何全局作用域变量(字段),只有局部变量,那么如果编译器执行方法的主体,则它只能访问堆栈中位于该方法主体内的对象。它无法访问其他局部变量,因为它们超出了范围。该方法完成并返回后,将弹出堆栈顶部,并且活动范围会更改。

也许您注意到在上图中,显示了多个堆栈存储器。这是因为Java的堆栈内存是按线程分配的。因此,每次创建和启动一个线程时,它都有自己的堆栈内存-并且无法访问另一个线程的堆栈内存。


-

内存的这一部分将实际对象存储在内存中。这些由堆栈中的变量引用。例如,让我们分析以下代码行中发生的情况:

StringBuilder builder = new StringBuilder(); 

该 new关键字是负责确保有上堆足够的自由空间,在存储器中创建StringBuilder的类型的对象,并通过“助洗剂”的参考,其进入堆叠上提到它。

每个正在运行的JVM进程只有一个堆内存。因此,无论正在运行多少线程,这都是内存的共享部分。实际上,堆结构与上图所示有所不同。堆本身分为几部分,这简化了垃圾回收的过程。

未预定义最大堆栈和堆大小-这取决于运行的计算机。但是,在本文后面,我们将研究一些JVM配置,这些配置将允许我们为正在运行的应用程序显式指定它们的大小。

参考类型

如果仔细查看“内存结构”图片,您可能会注意到,表示对堆中对象的引用的箭头实际上是不同类型的。这是因为在Java编程语言中,我们有不同类型的引用:强引用,弱引用,软 引用和幻像 引用。引用类型之间的区别在于,它们引用的堆上的对象可以在不同条件下进行垃圾回收。让我们仔细看看它们中的每一个。

1.强有力的参考

这些是我们大家都习惯的最流行的引用类型。在上面使用StringBuilder的示例中,我们实际上对堆中的对象持有强大的引用。堆上的对象不是有垃圾回收的,而是有一个指向它的强引用,或者通过一串强引用可以很强地到达该对象。

2.参考不足

简单来说,在下一个垃圾回收过程之后,对堆中对象的弱引用最有可能无法幸免。弱引用的创建方式如下:

WeakReference<StringBuilder> reference = new WeakReference<>(new StringBuilder()); 

弱引用的一个很好的用例是缓存方案。想象一下,您检索了一些数据,并且还希望将其存储在内存中-可以再次请求相同的数据。另一方面,您不确定何时或是否将再次请求此数据。因此,您可以对其进行弱引用,以防万一垃圾收集器运行,可能是它破坏了堆上的对象。因此,过一会儿,如果您想检索所引用的对象,则可能会突然取回一个 null值。缓存方案的一个很好的实现是集合WeakHashMap <K,V>。如果我们WeakHashMap在Java API中打开该类,则会看到其条目实际上扩展了 WeakReference该类,并使用其ref 字段作为映射的键:

/**
    * The entries in this hash table extend WeakReference, using its main ref
    * field as the key.
*/

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {

    V value;

一旦从WeakHashMap提取了一个键,就将整个条目从映射中删除。

3.软参考

这些类型的引用用于对内存更敏感的方案,因为只有当您的应用程序内存不足时,这些引用才会被垃圾回收。因此,只要没有必要释放空间,垃圾收集器就不会接触柔软可触及的对象。Java保证所有软引用的对象在引发之前都会被清除OutOfMemoryError。Javadocs指出:“确保在虚拟机引发OutOfMemoryError之前,已清除所有对可软访问的对象的软引用。”

与弱引用类似,按如下方式创建软引用:

SoftReference<StringBuilder> reference = new SoftReference<>(new StringBuilder());

4.幻影参考

因为我们可以确定对象不再存在,所以用于计划事后清理操作。仅与引用队列一起使用,因为.get()此类引用的方法将始终返回null。这些类型的引用被认为比终结器更可取。

如何引用字符串

Java中的类型有一些区别。字符串是不可变的,这意味着每次使用字符串进行操作时,实际上都会在堆上创建另一个对象。对于字符串,Java管理内存中的字符串池。这意味着Java会尽可能存储和重用字符串。对于字符串文字而言,大多数情况下都是这样。例如: String

String localPrefix = "297"; //1
String prefix = "297";      //2

if (prefix == localPrefix)
{
    System.out.println("Strings are equal" );
}
else
{
    System.out.println("Strings are different");
}

运行时,将打印出以下内容:

字符串相等

因此,事实证明,在比较了String类型的两个引用之后,这些引用实际上指向堆上的相同对象。但是,这对于计算的字符串无效。假设我们在上面的代码的// 1行中进行了以下更改

String localPrefix = new Integer(297).toString(); //1

输出:

字符串不同

在这种情况下,我们实际上看到堆上有两个不同的对象。如果我们认为计算字符串会经常使用,则可以通过.intern()在计算字符串的末尾添加方法来强制JVM将其添加到字符串池中:

String localPrefix = new Integer(297).toString().intern(); //1

添加以上更改将创建以下输出:

字符串相等

垃圾收集程序

如前所述,根据堆栈中变量保存到堆中对象的引用的类型,在某个时间点,该对象将成为垃圾收集器的合格对象。

例如,所有红色的对象都有资格被垃圾收集器收集。您可能会注意到堆上有一个对象,该对象对堆上也有其他对象具有强引用(例如,可以是对其项进行引用的列表,或者是具有两个引用类型字段的对象)。但是,由于堆栈中的引用丢失了,因此无法再对其进行访问,因此它也是垃圾。

为了更深入地讨论细节,让我们首先提到一些事情:

此过程由Java自动触发,由Java决定何时以及是否启动此过程。

这实际上是一个昂贵的过程。当垃圾收集器运行时,应用程序中的所有线程都将暂停(取决于GC类型,这将在后面讨论)。

实际上,这不仅仅是一个垃圾收集和释放内存的过程,更复杂的过程。

即使Java决定了何时运行垃圾回收器,您也可以显式调用System.gc()并期望垃圾回收器在执行此行代码时将运行,对吗?

这是一个错误的假设。

您只是要求Java运行垃圾收集器,但还是由它决定是否执行此操作。无论如何,System.gc()不建议显式调用 。

由于这是一个非常复杂的过程,并且可能会影响您的性能,因此可以以一种聪明的方式来实现它。为此使用了所谓的“标记和扫描”过程。Java分析堆栈中的变量,并“标记”所有需要保持活动状态的对象。然后,清除所有未使用的对象。

因此,实际上,Java不会收集任何垃圾。实际上,垃圾越多,对象被标记为活动的越少,该过程就越快。为了使其更加优化,堆内存实际上由多个部分组成。我们可以使用Java JDK附带的工具JVisualVM可视化内存使用情况和其他有用的东西。唯一要做的就是安装一个名为Visual GC的插件,该插件可让您查看内存的实际结构。让我们放大一下并分解大图:

创建对象后,将在Eden(1)空间上分配该对象。由于伊甸园空间并不大,因此充满空间很快。垃圾收集器在Eden空间上运行,并将对象标记为活动。

一旦对象在垃圾回收过程中幸存下来,就将其移动到所谓的幸存者空间S0(2)中。垃圾收集器第二次在Eden空间上运行时,会将所有幸存的对象移动到S1(3)空间中。同样,当前在S0(2)上的所有内容都移到S1(3)空间中。

如果一个对象能够幸存X轮垃圾回收(X取决于JVM实现,在我的例子中是8),则它很可能永远存在,并被转移到Old(4)空间中。

到目前为止,如果您查看所有垃圾回收器graph(6),则每次运行时,您都可以看到对象切换到幸存者空间,而Eden空间获得了空间。等等等等。老一代也可以进行垃圾回收,但是由于与Eden空间相比,它占了内存的更大部分,因此这种情况很少发生。在元空间(5)用于元数据存储有关的JVM您加载的类。

所显示的图片实际上是Java 8应用程序。在Java 8之前,内存的结构有些不同。元空间实际上称为PermGen。空间。例如,在Java 6中,该空间还存储了字符串池的内存。因此,如果Java 6应用程序中的字符串太多,则可能会崩溃。

垃圾收集器类型

实际上,JVM具有三种类型的垃圾收集器,程序员可以选择应使用哪种类型。默认情况下,Java根据基础硬件选择要使用的垃圾收集器类型。

1.串行GC –单线程收集器。通常适用于数据量少的小型应用程序。可以通过指定命令行选项来启用:-XX:+UseSerialGC

2.并行GC –甚至从名称上来说,串行和并行之间的区别在于,并行GC使用多个线程来执行垃圾收集过程。这种GC类型也称为吞吐量收集器。可以通过显式指定以下选项来启用它:-XX:+UseParallelGC

3.主要是并发GC –如果您还记得,在本文前面,曾提到垃圾收集过程实际上是非常昂贵的,并且在运行时,所有线程都将暂停。但是,我们有这种主要是并发的GC类型,表明它与应用程序并发工作。但是,有一个原因导致它“大部分”是并发的。它不适用于该应用程序100%并发运行。在一段时间内线程被暂停。尽管如此,暂停时间要保持得尽可能短,以实现最佳的GC性能。实际上,主要有两种并发GC:

3.1垃圾优先–高吞吐量和合理的应用程序暂停时间。通过以下选项启用:-XX:+UseG1GC

3.2并发标记扫描–应用程序暂停时间保持最短。可以通过指定以下选项来使用它:-XX:+UseConcMarkSweepGC。从JDK 9开始,不建议使用此GC类型。

技巧和窍门

为了最大程度地减少内存占用,请尽可能限制变量的范围。请记住,每次弹出堆栈顶部的作用域时,该作用域中的引用都会丢失,这会使对象有资格进行垃圾收集。

明确引用null 过时的参考。这将使那些引用的对象有资格进行垃圾收集。

避免终结器。他们放慢了过程,他们不保证任何事情。最好使用幻像引用进行清理工作。

不要在使用弱引用或软引用的地方使用强引用。最常见的内存陷阱是​​缓存方案,即即使可能不需要数据,也将其保存在内存中。

JVisualVM还具有在特定点进行堆转储的功能,因此您可以针对每个类分析其占用的内存量。

根据您的应用程序需求配置JVM。在运行应用程序时,明确指定JVM的堆大小。内存分配过程也很昂贵,因此请为堆分配合理的初始和最大内存量。如果您知道从一开始就以较小的初始堆大小开始是没有意义的,那么JVM将扩展此内存空间。使用以下选项指定内存选项:

初始堆大小-Xms512m–将初始堆大小设置为512 MB。

最大堆大小-Xmx1024m–将最大堆大小设置为1024 MB。

线程堆栈大小-Xss1m–将线程堆栈大小设置为1 MB。

年轻代大小-Xmn256m–将年轻代大小设置为256 MB。

如果Java应用程序使用崩溃, OutOfMemoryError并且您需要一些其他信息来检测泄漏,请使用–XX:HeapDumpOnOutOfMemory参数运行该过程,该 参数将在下次发生此错误时创建一个堆转储文件。

使用该 -verbose:gc选项获取垃圾回收输出。每次进行垃圾收集时,都会生成一个输出。

结论

知道内存的组织方式可以使您从内存资源上编写良好且经过优化的代码,从而具有优势。有利的是,您可以通过提供最适合正在运行的应用程序的不同配置来调整正在运行的JVM。如果使用正确的工具,发现并修复内存泄漏只是一件容易的事。

参考:《2020最新Java基础精讲视频教程和学习路线!》

链接: Java内存管理_didiao java的博客-CSDN博客
java 程序员 spring 后端 springboot
阅读 28 发布于 3 月 2 日
收藏
分享
本作品系原创, 采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议
avatar
Java攻城师

本人太过于丰富,无法简介

245 声望
353 粉丝
关注作者
0 条评论
得票 时间
提交评论
avatar
Java攻城师

本人太过于丰富,无法简介

245 声望
353 粉丝
关注作者
宣传栏
目录

对Java内存管理的深入了解将增强您对堆的工作方式,引用类型和垃圾回收的了解。

您可能会认为,如果您使用Java进行编程,那么您需要了解有关内存工作原理的哪些信息?Java具有自动内存管理功能,这是一个不错的安静的垃圾回收器,它在后台工作以清理未使用的对象并释放一些内存。

因此,作为Java程序员,您不再需要像销毁对象那样麻烦自己,因为不再使用它们。但是,即使该过程在Java中是自动的,也不能保证任何事情。通过不知道垃圾收集器和Java内存的设计方式,即使您不再使用垃圾回收器和Java内存,您也可能拥有不符合垃圾收集条件的对象。

因此,了解内存在Java中的实际工作方式非常重要,因为它为您提供了编写高性能和优化应用程序的优势,这些应用程序永远不会因崩溃而崩溃OutOfMemoryError。另一方面,当您发现自己的状况很糟时,您将能够快速找到内存泄漏。

首先,让我们看一下内存通常是如何用Java组织的:

通常,内存分为两大部分:堆栈和堆。 请记住,此图片中的内存类型大小与实际内存大小不成比例。与堆栈相比,堆是大量的内存。

堆栈

堆栈内存负责保存对堆对象的引用,并负责存储值类型(在Java中也称为原始类型),这些值类型本身保存值,而不是对堆中对象的引用。

另外,堆栈上的变量具有一定的可见性,也称为scope。仅使用活动范围中的对象。例如,假设我们没有任何全局作用域变量(字段),只有局部变量,那么如果编译器执行方法的主体,则它只能访问堆栈中位于该方法主体内的对象。它无法访问其他局部变量,因为它们超出了范围。该方法完成并返回后,将弹出堆栈顶部,并且活动范围会更改。

也许您注意到在上图中,显示了多个堆栈存储器。这是因为Java的堆栈内存是按线程分配的。因此,每次创建和启动一个线程时,它都有自己的堆栈内存-并且无法访问另一个线程的堆栈内存。


-

内存的这一部分将实际对象存储在内存中。这些由堆栈中的变量引用。例如,让我们分析以下代码行中发生的情况:

StringBuilder builder = new StringBuilder(); 

该 new关键字是负责确保有上堆足够的自由空间,在存储器中创建StringBuilder的类型的对象,并通过“助洗剂”的参考,其进入堆叠上提到它。

每个正在运行的JVM进程只有一个堆内存。因此,无论正在运行多少线程,这都是内存的共享部分。实际上,堆结构与上图所示有所不同。堆本身分为几部分,这简化了垃圾回收的过程。

未预定义最大堆栈和堆大小-这取决于运行的计算机。但是,在本文后面,我们将研究一些JVM配置,这些配置将允许我们为正在运行的应用程序显式指定它们的大小。

参考类型

如果仔细查看“内存结构”图片,您可能会注意到,表示对堆中对象的引用的箭头实际上是不同类型的。这是因为在Java编程语言中,我们有不同类型的引用:强引用,弱引用,软 引用和幻像 引用。引用类型之间的区别在于,它们引用的堆上的对象可以在不同条件下进行垃圾回收。让我们仔细看看它们中的每一个。

1.强有力的参考

这些是我们大家都习惯的最流行的引用类型。在上面使用StringBuilder的示例中,我们实际上对堆中的对象持有强大的引用。堆上的对象不是有垃圾回收的,而是有一个指向它的强引用,或者通过一串强引用可以很强地到达该对象。

2.参考不足

简单来说,在下一个垃圾回收过程之后,对堆中对象的弱引用最有可能无法幸免。弱引用的创建方式如下:

WeakReference<StringBuilder> reference = new WeakReference<>(new StringBuilder()); 

弱引用的一个很好的用例是缓存方案。想象一下,您检索了一些数据,并且还希望将其存储在内存中-可以再次请求相同的数据。另一方面,您不确定何时或是否将再次请求此数据。因此,您可以对其进行弱引用,以防万一垃圾收集器运行,可能是它破坏了堆上的对象。因此,过一会儿,如果您想检索所引用的对象,则可能会突然取回一个 null值。缓存方案的一个很好的实现是集合WeakHashMap <K,V>。如果我们WeakHashMap在Java API中打开该类,则会看到其条目实际上扩展了 WeakReference该类,并使用其ref 字段作为映射的键:

/**
    * The entries in this hash table extend WeakReference, using its main ref
    * field as the key.
*/

private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {

    V value;

一旦从WeakHashMap提取了一个键,就将整个条目从映射中删除。

3.软参考

这些类型的引用用于对内存更敏感的方案,因为只有当您的应用程序内存不足时,这些引用才会被垃圾回收。因此,只要没有必要释放空间,垃圾收集器就不会接触柔软可触及的对象。Java保证所有软引用的对象在引发之前都会被清除OutOfMemoryError。Javadocs指出:“确保在虚拟机引发OutOfMemoryError之前,已清除所有对可软访问的对象的软引用。”

与弱引用类似,按如下方式创建软引用:

SoftReference<StringBuilder> reference = new SoftReference<>(new StringBuilder());

4.幻影参考

因为我们可以确定对象不再存在,所以用于计划事后清理操作。仅与引用队列一起使用,因为.get()此类引用的方法将始终返回null。这些类型的引用被认为比终结器更可取。

如何引用字符串

Java中的类型有一些区别。字符串是不可变的,这意味着每次使用字符串进行操作时,实际上都会在堆上创建另一个对象。对于字符串,Java管理内存中的字符串池。这意味着Java会尽可能存储和重用字符串。对于字符串文字而言,大多数情况下都是这样。例如: String

String localPrefix = "297"; //1
String prefix = "297";      //2

if (prefix == localPrefix)
{
    System.out.println("Strings are equal" );
}
else
{
    System.out.println("Strings are different");
}

运行时,将打印出以下内容:

字符串相等

因此,事实证明,在比较了String类型的两个引用之后,这些引用实际上指向堆上的相同对象。但是,这对于计算的字符串无效。假设我们在上面的代码的// 1行中进行了以下更改

String localPrefix = new Integer(297).toString(); //1

输出:

字符串不同

在这种情况下,我们实际上看到堆上有两个不同的对象。如果我们认为计算字符串会经常使用,则可以通过.intern()在计算字符串的末尾添加方法来强制JVM将其添加到字符串池中:

String localPrefix = new Integer(297).toString().intern(); //1

添加以上更改将创建以下输出:

字符串相等

垃圾收集程序

如前所述,根据堆栈中变量保存到堆中对象的引用的类型,在某个时间点,该对象将成为垃圾收集器的合格对象。

例如,所有红色的对象都有资格被垃圾收集器收集。您可能会注意到堆上有一个对象,该对象对堆上也有其他对象具有强引用(例如,可以是对其项进行引用的列表,或者是具有两个引用类型字段的对象)。但是,由于堆栈中的引用丢失了,因此无法再对其进行访问,因此它也是垃圾。

为了更深入地讨论细节,让我们首先提到一些事情:

此过程由Java自动触发,由Java决定何时以及是否启动此过程。

这实际上是一个昂贵的过程。当垃圾收集器运行时,应用程序中的所有线程都将暂停(取决于GC类型,这将在后面讨论)。

实际上,这不仅仅是一个垃圾收集和释放内存的过程,更复杂的过程。

即使Java决定了何时运行垃圾回收器,您也可以显式调用System.gc()并期望垃圾回收器在执行此行代码时将运行,对吗?

这是一个错误的假设。

您只是要求Java运行垃圾收集器,但还是由它决定是否执行此操作。无论如何,System.gc()不建议显式调用 。

由于这是一个非常复杂的过程,并且可能会影响您的性能,因此可以以一种聪明的方式来实现它。为此使用了所谓的“标记和扫描”过程。Java分析堆栈中的变量,并“标记”所有需要保持活动状态的对象。然后,清除所有未使用的对象。

因此,实际上,Java不会收集任何垃圾。实际上,垃圾越多,对象被标记为活动的越少,该过程就越快。为了使其更加优化,堆内存实际上由多个部分组成。我们可以使用Java JDK附带的工具JVisualVM可视化内存使用情况和其他有用的东西。唯一要做的就是安装一个名为Visual GC的插件,该插件可让您查看内存的实际结构。让我们放大一下并分解大图:

创建对象后,将在Eden(1)空间上分配该对象。由于伊甸园空间并不大,因此充满空间很快。垃圾收集器在Eden空间上运行,并将对象标记为活动。

一旦对象在垃圾回收过程中幸存下来,就将其移动到所谓的幸存者空间S0(2)中。垃圾收集器第二次在Eden空间上运行时,会将所有幸存的对象移动到S1(3)空间中。同样,当前在S0(2)上的所有内容都移到S1(3)空间中。

如果一个对象能够幸存X轮垃圾回收(X取决于JVM实现,在我的例子中是8),则它很可能永远存在,并被转移到Old(4)空间中。

到目前为止,如果您查看所有垃圾回收器graph(6),则每次运行时,您都可以看到对象切换到幸存者空间,而Eden空间获得了空间。等等等等。老一代也可以进行垃圾回收,但是由于与Eden空间相比,它占了内存的更大部分,因此这种情况很少发生。在元空间(5)用于元数据存储有关的JVM您加载的类。

所显示的图片实际上是Java 8应用程序。在Java 8之前,内存的结构有些不同。元空间实际上称为PermGen。空间。例如,在Java 6中,该空间还存储了字符串池的内存。因此,如果Java 6应用程序中的字符串太多,则可能会崩溃。

垃圾收集器类型

实际上,JVM具有三种类型的垃圾收集器,程序员可以选择应使用哪种类型。默认情况下,Java根据基础硬件选择要使用的垃圾收集器类型。

1.串行GC –单线程收集器。通常适用于数据量少的小型应用程序。可以通过指定命令行选项来启用:-XX:+UseSerialGC

2.并行GC –甚至从名称上来说,串行和并行之间的区别在于,并行GC使用多个线程来执行垃圾收集过程。这种GC类型也称为吞吐量收集器。可以通过显式指定以下选项来启用它:-XX:+UseParallelGC

3.主要是并发GC –如果您还记得,在本文前面,曾提到垃圾收集过程实际上是非常昂贵的,并且在运行时,所有线程都将暂停。但是,我们有这种主要是并发的GC类型,表明它与应用程序并发工作。但是,有一个原因导致它“大部分”是并发的。它不适用于该应用程序100%并发运行。在一段时间内线程被暂停。尽管如此,暂停时间要保持得尽可能短,以实现最佳的GC性能。实际上,主要有两种并发GC:

3.1垃圾优先–高吞吐量和合理的应用程序暂停时间。通过以下选项启用:-XX:+UseG1GC

3.2并发标记扫描–应用程序暂停时间保持最短。可以通过指定以下选项来使用它:-XX:+UseConcMarkSweepGC。从JDK 9开始,不建议使用此GC类型。

技巧和窍门

为了最大程度地减少内存占用,请尽可能限制变量的范围。请记住,每次弹出堆栈顶部的作用域时,该作用域中的引用都会丢失,这会使对象有资格进行垃圾收集。

明确引用null 过时的参考。这将使那些引用的对象有资格进行垃圾收集。

避免终结器。他们放慢了过程,他们不保证任何事情。最好使用幻像引用进行清理工作。

不要在使用弱引用或软引用的地方使用强引用。最常见的内存陷阱是​​缓存方案,即即使可能不需要数据,也将其保存在内存中。

JVisualVM还具有在特定点进行堆转储的功能,因此您可以针对每个类分析其占用的内存量。

根据您的应用程序需求配置JVM。在运行应用程序时,明确指定JVM的堆大小。内存分配过程也很昂贵,因此请为堆分配合理的初始和最大内存量。如果您知道从一开始就以较小的初始堆大小开始是没有意义的,那么JVM将扩展此内存空间。使用以下选项指定内存选项:

初始堆大小-Xms512m–将初始堆大小设置为512 MB。

最大堆大小-Xmx1024m–将最大堆大小设置为1024 MB。

线程堆栈大小-Xss1m–将线程堆栈大小设置为1 MB。

年轻代大小-Xmn256m–将年轻代大小设置为256 MB。

如果Java应用程序使用崩溃, OutOfMemoryError并且您需要一些其他信息来检测泄漏,请使用–XX:HeapDumpOnOutOfMemory参数运行该过程,该 参数将在下次发生此错误时创建一个堆转储文件。

使用该 -verbose:gc选项获取垃圾回收输出。每次进行垃圾收集时,都会生成一个输出。

结论

知道内存的组织方式可以使您从内存资源上编写良好且经过优化的代码,从而具有优势。有利的是,您可以通过提供最适合正在运行的应用程序的不同配置来调整正在运行的JVM。如果使用正确的工具,发现并修复内存泄漏只是一件容易的事。

参考:《2020最新Java基础精讲视频教程和学习路线!》

链接: Java内存管理_didiao java的博客-CSDN博客