草庐IT

JVM垃圾清除算法

小威要向诸佬学习呀 2024-02-21 原文

前言:大家好,我是小威,24届毕业生,在一家满意的公司实习。本篇将记录几次面试中经常被问到的知识点以及对学习的知识点总结和面试题的复盘。
本篇文章记录的基础知识,适合在学Java的小白,也适合复习中,面试中的大佬🤩🤩。
如果文章有什么需要改进的地方还请大佬不吝赐教👏👏。
小威在此先感谢各位大佬啦~~🤞🤞

🏠个人主页:小威要向诸佬学习呀
🧑个人简介:大家好,我是小威,一个想要与大家共同进步的男人😉😉
目前状况🎉:24届毕业生,在一家满意的公司实习👏👏

🎁如果大佬在准备面试,可以使用我找实习前用的刷题神器哦刷题神器点这里哟
💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,我亲爱的大佬😘

以下正文开始

文章目录

🎄JVM线程私有和共享的区域

JVM线程私有的区域有:虚拟机栈,本地方法栈,程序计数器

虚拟机栈:主要存储方法,局部变量,运行的数据。
本地方法栈:主要存储本地方法(含有Native关键字的方法)。
程序计数器:存储程序运行位置的字节码行号指示器。

JVM线程共享的区域有:Java堆,元空间

Java堆:存储所有创建的对象,数组等。
元空间:存储虚拟机加载的字节码数据,常量,静态变量,运行时常量池等。

🎇线程上下文切换

线程上下文切换,也就是CPU不再执行当前的线程,而去执行其他的线程。那有哪些原因会导致线程的上下文切换呢?

  1. 线程的时间片用完
  2. 垃圾回收(会暂停当前工作的线程,先进行垃圾回收)
  3. 更高优先级的线程运行
  4. 线程主动调用了某些方法,如sleep,yeild,wait,join,synchronized,lock等

当发生上下文切换时,操作系统会保存当前线程的状态,恢复另一个线程的状态,此时程序计数器会记住下一条jvm指令的执行地址,同时上文记录,程序计数器是线程私有的

🍒如何判断对象是否存活

判断对象是否存活有两种方法:引用计数算法和可达性分析算法

🍸引用计数法

在对象被创建的时候,会在对象头中分配一个空间,即计时器,来保存这个对象被引用的次数。如果这个对象被其他的对象引用,它的引用计数器会+1,如果删除其他对象对这个对象的引用,则它的引用计数会-1,当对象的引用计数为0时,这个对象就会被当成垃圾回收。

优点:
引用计数法实现起来比较简单,判断对象是否存活的效率比较高。
缺点:
无法解决对象之间循环引用的问题,不能检测到环的出现。例如,A和B之间相互引用,此时计数器都会显示为1,此时A和B都无法进行垃圾回收。

🎍可达性分析法

Java虚拟机中的垃圾回收机制都是采用的可达性分析算法来探索存活的对象的。此种方法工作原理是会扫描java堆中的对象,沿着GC Roots对象往下寻找,看看是否能在此引用链中找到该对象,如果找不到的话,证明该对象没用了,表示该对象可以回收。

可达性分析算法最大的优点之一就是解决了对象之间的相互循环依赖的问题,目前和引用计数法比起来没有缺点

🍖JVM中的垃圾回收算法

对于新生代和老年代的对象,在JVM中会采取不同的垃圾回收算法。年轻代的对象一般都是朝生暮死的,创建之后很快就会被回收,而老年代的对象是需要长期存活的,因此用到的算法大不相同。新生代对应的收集方法为“Minor GC”,老年代对应的收集方法称为“Major GC”,而对于整个堆空间和方法区的回收被称为“Full GC”

🧃标记清除算法

标记清除算法为最基础的垃圾收集算法,即为每个对象都分配一个标记为,这个标记位会记录对象的状态。标记着所要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以标记存活的对象,清理掉未标记的对象。标记清除算法用于老年代的垃圾回收中

优点: 基于可达性分析算法,实现起来比较简单,后续的算法都是基于这种思想来实现的。
缺点:
影响最大的一点在于,标记清除算法会使内存空间碎片化,即标记并清除垃圾后,会产生很多不连续的内存空间,这将导致较大的对象因为无法找到连续的内存而提前触发一次垃圾回收。

如果大部分对象需要回收,就会进行大量的标记和清除操作,存活对象数量多时效率会降低

🥫复制算法

复制算法被用于新生代的垃圾回收机制中,新生代有三部分,Eden(80%),和两个survivor区(From Survivor 和 To Survivor)。两个Survivor区为容量大小相等的两块内存,每次只使用其中的一块内存,当使用的那块内存用完后,就会将内存中还存活着的对象复制到另一块内存上,然后把使用过的那块内存空间清空。

优点: 实现起来比较简单,效率也比较高,可以保证内存有连续的区域能够解决标记清除算法导致的内存碎片问题。
缺点:
可分配的内存空间缩小了一半儿,代价比较高,内存空间浪费比较多; 存活的对象比较多的时候使用复制算法将会导致效率降低。
进行标记清除算法时,会导致应用程序挂起(停顿),即stop the world(STW)。

扩展:
90%以上的对象都是朝生暮死的,所以在新生代中,每次为对象分配内存时会使用Eden区和其中的一块Survivor区,当发生垃圾回收时,JVM会将Eden和Survivor中存活的对象都复制到另一块Survivor区域内,之后清理掉Eden区和Survivor区域中的空间。综上所述,建立新对象时,新生代可用内存空间为整个儿新生代容量的90%(80%的Eden区和10%的Survivor区),如果发生了极少部分情况,即多于10%的对象存活下来了,没有被垃圾回收器回收掉,此时JVM会触发空间担保机制,即当Survivor空间不足以容纳一次Minor GC后的存活对象时,就需要依赖老年代进行分配担保。

🥓标记整理算法

标记整理法是对标记清除算法的一个改进。第一个阶段和标记清除算法一样,都是将对象标记为存活和死亡状态,然而在第二阶段,标记清除算法只是将被标记的对象进行清除,标记整理算法会将存活的对象进行整理并且放到另一个端,然后再把所有的对象清除掉。
标记整理算法用于老年代的回收机制中

优点: 不会像垃圾清除算法那样产生不连续的内存碎片空间
不会像复制算法那样划分两个区域,提高了空间的利用率
缺点:
效率上肯定更慢一些,因为多了一步整理的操作过程。

🍨如何判断变量是否线程安全

对于成员变量和静态变量

  • 如果它们没有被共享,则它们是线程安全的;
  • 如果它们被共享了,根据它们的状态是否能够改变,又会分两种情况:如果只有读操作,则它们是线程安全的;如果有读写操作,则这段代码是临界区,是需要考虑线程安全的。

对于局部变量是否线程安全

  • 局部变量是线程安全的
  • 但局部变量引用的对象则未必线程安全。如果该对象没有逃离方法的作用访问,它是线程安全的;如果该对象逃离方法的作用范围,则是需要考虑线程安全的。

🍻最长递增子序列

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4

示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

思路:该题让求出最长的递增子序列,因此至少需要一次遍历,考虑到代码进行到每一步的状态才可以,所以动态规划法解决此题比较容易。

代码+详解:

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums.length==0){
            return 0; //长度为0直接返回0
        }
        int [] dp=new int[nums.length];
        dp[0]=1; //初始化数组,也可以调用库函数Arrays.fill(dp,1),但是效率会慢些
        int result=1;//初始化结果
        for(int i=1;i<nums.length;i++){
            dp[i]=1;//初始化数组
            for(int j=0;j<i;j++){
                if(nums[j]<nums[i]){
                dp[i]=Math.max(dp[i],dp[j]+1);//循环更新dp[i]的最大值
                }
            }
            result=Math.max(result,dp[i]);
        }
        return result;
    }
}

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起讨论🍻
希望能和诸佬们一起努力,今后进入到心仪的公司
再次感谢各位小伙伴儿们的支持🤞

有关JVM垃圾清除算法的更多相关文章

  1. ruby - 如何在 Ubuntu 中清除 Ruby Phusion Passenger 的缓存? - 2

    我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:

  2. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  3. ruby-on-rails - 清除 GitLab 中的所有 Assets - 2

    我想在我公司安装的GitLab中使用自定义Logo-白色、Logo-黑色和网站图标。我用谷歌搜索了我的屁股并尝试了所有我能找到的方法来清除这些该死的图像,但似乎没有任何效果。这是唯一似乎成功运行但未删除图像的进程:bundleexecrakecache:clearRAILS_ENV=productionservicegitlabstopredis-cliFLUSHALLbundleexecrakeassets:precompileRAILS_ENV=productionservicegitlabstart然后我清除我的浏览器缓存并转到该域,再次出现相同的该死的图像!我什至删除了我能从应

  4. 100个python算法超详细讲解:画直线 - 2

    1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva

  5. ruby - 在 Ruby 中实现 Luhn 算法 - 2

    我一直在尝试用Ruby实现Luhn算法。我一直在执行以下步骤:该公式根据其包含的校验位验证数字,该校验位通常附加到部分帐号以生成完整帐号。此帐号必须通过以下测试:从最右边的校验位开始向左移动,每第二个数字的值加倍。将乘积的数字(例如,10=1+0=1、14=1+4=5)与原始数字的未加倍数字相加。如果总模10等于0(如果总和以零结尾),则根据Luhn公式该数字有效;否则无效。http://en.wikipedia.org/wiki/Luhn_algorithm这是我想出的:defvalidCreditCard(cardNumber)sum=0nums=cardNumber.to_s.s

  6. Ruby 斐波那契算法 - 2

    下面是我写的一个计算斐波那契数列中的值的方法:deffib(n)ifn==0return0endifn==1return1endifn>=2returnfib(n-1)+(fib(n-2))endend它工作到n=14,但在那之后我收到一条消息说程序响应时间太长(我正在使用repl.it)。有人知道为什么会这样吗? 最佳答案 Naivefibonacci进行了大量的重复计算-在fib(14)fib(4)中计算了很多次。您可以将内存添加到您的算法中以使其更快:deffib(n,memo={})ifn==0||n==1returnnen

  7. ruby-on-rails - RailsTutorial - 第 8.4.3 章 - 在集成测试中添加用户后未清除测试数据库 - 2

    我被这个难住了。到目前为止教程中的一切都进行得很顺利,但是当我将这段代码添加到我的/spec/requests/users_spec.rb文件中时,事情开始变得糟糕:describe"success"doit"shouldmakeanewuser"dolambdadovisitsignup_pathfill_in"Name",:with=>"ExampleUser"fill_in"Email",:with=>"ryan@example.com"fill_in"Password",:with=>"foobar"fill_in"Confirmation",:with=>"foobar"cl

  8. ruby-on-rails - 为什么 Devise/Omniauth 会向 URL 添加垃圾? - 2

    使用facebook登录后,我被重定向到/#_=_,其中显示主页。这种垃圾也出现在其他URL中,例如当注册失败并被重定向到/users/sign_in#_=_为什么会发生这种情况,我该如何解决? 最佳答案 如果你真的不想要它,一些简单的javascript就可以了:if(window.location.hash=="#_=_"){window.location.hash="";} 关于ruby-on-rails-为什么Devise/Omniauth会向URL添加垃圾?,我们在StackO

  9. ruby-on-rails - ActionMailer HTML 编码 hell - 特殊字符替换为垃圾 - 2

    我有UTF-8字符串:Website•Facebook那是中间的一颗子弹又名•或0xE20x800xA2此值已正确存储在数据库中,并使用默认设置使用Rails3和ruby​​1.9.3正确显示在屏幕上。我正在尝试通过HTML电子邮件发送此邮件,但是当一切都说完之后,接收端看到的是垃圾:这背后的代码很简单,我有一个ActionMailer子类(默认使用UTF-8)设置以在布局中发送带有UTF-8内容编码的HTML电子邮件:email.html.erb布局文件:"all"%>内容使用与呈现网页相同的View,重要的一行是:我已经尝试了很多很多force_encoding的排列,e

  10. ruby-on-rails - Rails add_index 算法 : :concurrently still causes database lock up during migration - 2

    为了防止在迁移到生产站点期间出现数据库事务错误,我们遵循了https://github.com/LendingHome/zero_downtime_migrations中列出的建议。(具体由https://robots.thoughtbot.com/how-to-create-postgres-indexes-concurrently-in概述),但在特别大的表上创建索引期间,即使是索引创建的“并发”方法也会锁定表并导致该表上的任何ActiveRecord创建或更新导致各自的事务失败有PG::InFailedSqlTransaction异常。下面是我们运行Rails4.2(使用Acti

随机推荐