b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

jvm的垃圾回收器的作用及解决办法(上)

电脑杂谈  发布时间:2021-05-15 23:04:08  来源:网络整理

上一篇文章介绍了JVM内存模型的知识,本文将介绍垃圾回收的相关内容。

垃圾收集器的作用

1、对象分配和引用

首先让我们看一下代码片段,如下所示:

public class User {
        public static void main(String[] args) {
             loadDisk();
       }
       public static void loadDisk() {
             Role role = new Role();
             role.load();
       }
}

在上面的代码中,一旦“ role.load()”执行结束,该方法将被执行,并且相应的堆栈帧将从主线程的java虚拟机堆栈中弹出。这时,一旦从堆栈弹出loadDisk方法的堆栈框架,堆栈框架中的局部变量“ role”就消失了。换句话说,没有任何变量指向Java堆内存中的Role实例对象,并且该对象实际上是无用的。

每个人都知道内存资源有限。一般而言,当我们在服务器上启动Java系统时,机器的内存资源是有限的,例如4 GB的内存。当我们启动一个Java系统时,它实际上是一个jvm进程,因此jvm进程本身还将占用机器上的部分内存资源,例如1G的内存资源。

我们在jvm的Java堆内存中创建的对象的性质也占用了jvm的内存资源。例如,角色实例对象占用500字节的内存。因此,我们应该在这里清楚:在Java堆内存中创建的对象会占用内存资源,而内存资源是有限的。

2、如何处理不再需要的对象?

由于创建的对象没有被任何方法的局部变量引用,并且仍然占用有限的资源,因此我们应该如何处理呢?

每个人都必须想到它:JVM的垃圾回收机制。

JVM本身具有垃圾回收机制,该机制是在后台自动运行的线程。只要启动jvm进程,它就会有一个用于垃圾回收的后台线程。

如果实例对象没有指向它的任何方法局部变量,也没有指向它的类的任何静态变量(包括常量等),则垃圾回收线程将没有该实例对象指向指向。回收它,从内存中清除它,然后释放它占用的资源。

jvm生成模型

让我们继续看一下代码片段,内容如下:

public class User {
        public static void main(String[] args) {
             while(true) {
                 loadDisk();
                 Thread.sleep(1000);
             }
       }
       public static void loadDisk() {
             Role role = new Role();
             role.load();
       }
}

我们已对该代码段进行了一些修改。在main方法中,“ loadDisk”方法会定期执行。首先,一旦执行了main方法,main方法的堆栈框架将被推入main线程的java虚拟机堆栈中。然后,每次在while循环中,都会调用loadDisk方法以将方法的堆栈框架推入其自己的Java虚拟机堆栈中。然后,当执行“ loadDisk”方法时,将在Java堆内存中创建一个Role对象实例。在“ loadDisk”方法中,将通过“ role”局部变量引用实例对象,然后将执行Role对象的“ load”方法。

大多数对象的寿命很短而且很短

现在有问题。在上面的代码中,Role对象实际上具有较短的生存时间。一旦没有人引用Role对象,它将被jvm的垃圾回收线程回收以释放内存空间。重复地,角色对象是连续创建和回收的。实际上,我们大多数程序都是这样的对象。

少数受试者可以长期生存

但是让我们看一下另一个代码段:

public class User {
      private static Role role = new Role();
        public static void main(String[] args) {
             while(true) {
                 loadDisk();
                 Thread.sleep(1000);
             }
       }
       public static void loadDisk() {
             role.load();
       }
}

以上代码的含义是为User类定义一个静态变量,即角色。该User类位于jvm的方法区域中。然后,让角色引用在Java堆内存中创建的Role实例对象。此时,Role实例对象将始终由User类的静态变量引用,因此它将始终驻留在Java堆内存中,并且不会被垃圾回收。

年轻一代和老一代

现在每个人都已经看到,根据编写代码的方式,可以使用不同的方法来创建和使用对象。实际上,对象的生命周期是不同的。因此,JVM将Java堆内存分为两个区域,一个是年轻的一代,另一个是老一代。

年轻一代将对象放在第一个代码示例中,这些对象在创建和使用后立即回收。

古老的做法是将第二种代码示例的类型放到创建后需要很长时间存在的对象中。

为什么我们需要区分年轻一代和老一代?

由于这与垃圾收集有关,对于年轻一代的对象,其特征是它们将在创建后立即被收集,因此需要一种垃圾收集算法。对象在年老时的特点是它们需要存在很长时间,因此需要另一种垃圾回收算法。因此有必要将其划分为两个区域以放置不同的对象。

什么是永久世代

这很简单。 jvm中的永久生成实际上是我们前面提到的方法领域。您可以将所谓的永久世代视为放置某种信息。

如何在jvm中分配对象?

大多数正常对象优先考虑年轻一代的内存分配

让我们回顾一下上面的代码片段。类静态变量“ role”引用的Role对象将存在很长一段时间,但是即使这样的对象也将在开始时通过“ new Role()”代码实例化。它也分配给了新一代。

新一代垃圾收集何时触发?

现在让我们看一个场景,每个人都应该知道,当执行该方法时,将弹出该方法的堆栈框架,这将导致没有局部变量引用先前创建的实例对象,那么它将立即进行垃圾收集。不应该回收Java堆中的任何一个使用实例对象吗?

不要以为它很简单,事实上,垃圾回收有其触发条件。

一种较常见的情况是这种情况。我们在方法中创建了大量对象。执行该方法后,将不再引用这些对象。随着程序的运行,此类对象将越来越多。那时,年轻一代的预分配存储空间几乎充满了对象。此时,代码将继续运行。它需要在年轻代中分配一个对象,并发现年轻代的存储空间不足。我该怎么办?

这时,将触发新一代的内存空间垃圾回收。新一代的内存垃圾回收也称为“次要GC”。有时我们称其为“年轻GC”。引用的垃圾对象被回收。

寿命长的物体

接下来,我们看下一个问题,它是类静态变量所引用的实例对象,它是一个长期存在的对象。例如,尽管新一代在系统运行时继续创建和回收对象,但是类静态变量引用的实例对象将始终存在并且不会被回收。

然后jvm现在有一条规则:

如果实例对象已成功在新一代中收集,并且在15次垃圾回收后仍未收集,则表明它已使用15年了。这是对象的年龄。每个垃圾收集都会执行。如果没有收集到物体,则将其寿命增加一年。 15次之后,它将被转移到Java堆内存的旧时代。顾名思义,旧时代就是存储这些非常古老的对象。

会回收旧一代的垃圾吗?

会否对旧有对象进行垃圾收集?

答案是肯定的!由于在代码运行时,旧的对象可能不再被任何变量引用,因此还需要对其进行垃圾回收。当越来越多的对象进入老年期时,一旦老年期已满,就会对老年期进行垃圾收集。至于回收的细节,我们将不在这里进行分析。

常用参数简介

讨论很多原理,让我们看一下如何详细设置jvm参数。

在JVM内存分配中,有几个参数是更核心的,如下所示:

1、 -Xms:Java堆内存大小

2、 -Xmx:Java堆内存的最大大小

3、 -Xmn:java堆内存中年轻一代的大小,扣除新一代后的旧一代的内存大小就是旧一代的内存大小

4、 -XX:PermSize:永久世代大小

5、 -XX:MaxPermSize:永久生成的最大大小

6、 -Xss:每个线程的堆栈内存大小

我们将在下面逐一解释以上参数。

-Xms和-Xmx用于设置Java堆内存的初始大小以及可以扩展到的最大大小。通常,对于这对参数,我们将设置相同的大小,关于细节,我们将在后面讨论。但是每个人都应该清楚,这两个参数用于限制Java堆内存的大小。

-Xmn是一个非常常见的参数。它用于在Java堆内存中设置年轻代的大小,然后减去年轻代大小后的剩余内存就是老代的内存大小。

-XX:PermSize和-XX:MaxPermSize分别限制了永久代的初始大小和永久代的最大大小。通常这两个参数值设置为相同,由于原因,我们将在后面介绍它们。

jvm堆dump文件分析_jvm堆内存溢出_jvm堆内存

如果它是jdk 1. 8之后的版本(包括1. 8版本),则将这两个参数替换为-XX:MetaspaceSize和-XX:MaxMetaspaceSize。

-Xss此参数限制每个线程的堆栈内存的大小,每个线程都有自己的虚拟机堆栈,然后每次执行一个方法时,该方法的堆栈框架将被推入线程堆栈。执行该方法后,将从线程的堆栈中弹出堆栈框架。

那么如何在部署系统上设置jvm参数?

实际上,这是相对简单的。例如,如果使用“ java -jar”方法在jar包中启动系统,则可以使用类似于以下格式:

java -Xmx512m -Xms512m -Xmn256m -XX:PermSize = 128m -XX:MaxPermSize = 128M -Xss1m -jar app.jar

如何为每天有数百万笔交易的电子商务支付系统设置JVM堆内存大小

每天处理数百万笔交易的电子商务支付系统,系统压力集中在哪里?我们应该知道,付款的核心部分是在用户发起付款请求时生成付款订单。此付款订单需要清楚记录。例如,谁发起了付款?支付哪种产品?哪个渠道用于付款?还有时间开始付款等等。

如果每天有数百万笔交易,那么它就会面临很多方面的压力,包括技术难题,例如高并发访问,高性能处理请求以及需要存储的大量付款订单数据,但是现在我们将这些系统架构方面放在一边,让我们看一下jvm级别。

支付系统的最大压力是每天在jvm内存中频繁创建和销毁一百万个支付订单,因此这里涉及一个核心问题。

我们需要计算的第一件事是系统每秒必须处理多少个付款订单。假设每天有1百万笔付款订单,那么一般的用户交易行为将发生在每天的高峰时段,例如中午或晚上。

假设每天的高峰时段大约为几个小时,并且一小时平均分配了100万个订单,则每秒大约有100个订单。然后,假设我们的支付系统部署了3台机器,每台机器实际上每秒处理大约30笔支付订单。

用户发起付款请求,付款需要在jvm中创建付款订单对象,填写数据,然后将其写入。其他事情可能会被处理。现在,我们假设付款请求的处理包含一个付款订单。创建大约需要1秒钟。

因此,现在我们基本上可以想到一个流畅的模型:每台机器在一秒钟内接收30个付款订单请求,然后在新一代JVM中创建30个付款订单对象,并进行处理,例如写入。 1秒后,将处理这30个付款订单,然后回收这些付款订单对象的引用,并且这些对象成为新一代JVM中的垃圾对象。然后在下一秒内发送30个付款单,重复此步骤。

实际上,在不考虑其他因素的情况下,我们可以对其进行粗略估计。例如,付款订单类别如下:

public class PayOrder {
      private Integer userId;
      private Long orderTime;
      private Integer orderId;
}

您只需要记住一个Integer类型的变量数据是4个字节,而Long类型的变量数据是8个字节。百度可以找到其他类型的变量数据占用多少字节。然后,您可以大致计算出每个付款订单对象占用多少字节。一般来说,对于付款订单之类的核心类,您可以基于20个实例变量进行计算,然后一个对象通常只有几百个字节。让我们考虑一下它。即使付款订单对象占用了500字节的存储空间,也将小于1 kb。

正如我之前所说,假设有3台机器每秒处理30个付款指令,那么在方法中必须在1秒内引用这些付款指令的局部变量。对于30个付款订单,占用的内存空间约为30 * 500字节= 15000字节,约为15kb,这实际上很小。

我们现在估计整个支付系统的内存使用情况。先前的分析全部基于核心业务流程中的付款订单对象。实际上,这只是一小部分。运行的真实支付系统肯定会每秒创建大量其他对象,但是我们可以通过结合访问压力和核心对象的内存占用来粗略估计整个支付系统每秒将占用多少内存空间。


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-375711-1.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...