潍坊网站建设SEO优化熊掌号/域名权重查询
【学习笔记】深入理解JVM之垃圾回收机制
更多文章首发地址:地址
参考:
- 《深入理解JAVA虚拟机》第三版 第三章
- 尚硅谷 第134 - 203 集
- 参考文章:https://blog.csdn.net/qq_48435252/article/details/123697193
1、概念
🌻 首先我们要知道在程序中
垃圾
是怎么样的一个定义?
在现实中,我们知道 垃圾
是我们人类丢弃的废弃物品,而在程序中也是相对如此 垃圾
是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾。 如果不及时对内存中的垃圾进行清理,那么,这些垃圾对象所占的内存空 间会一直保留到应用程序结束,被保留的空间无法被其他对象使用。甚至可能导致内存溢出。
🌻 为什么需要垃圾回收机制呢?
面对这个问题,我们也可以想一想我们人类自己的生存问题,如果我们一直不解决 垃圾
的存储问题,则可能会导致我们生存的地方被 垃圾
所侵占,从而导致没有生存空间。
- 对于高级语言来说,一个基本认知是,如果不进行
垃圾回收
,内存迟早都会被消耗完,因为不断地分配内存空间而不进行回收,就好像不停地生产生活垃圾
而从来不打扫一样。 - 除了释放没用的对象,
垃圾回收
也可以清除内存里的记录碎片。碎片整理将所占用的堆内存移到堆的一端,以便JVM
将整理出的内存分配给新的对象。
2、常见的垃圾回收算法
概念补充:
对象已死
在堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就 是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(“死去”即不可能再被任何途径使用的对 象)了。
- 判断对象存活一般有两种方式:
引用计数算法
和可达性分析算法
。
我们比较常见的 垃圾回收算法
有以下几种:
引用计数法
可达性分析算法
标记清除算法
复制算法
标记压缩算法
2.1 引用计数法
概念: 引用计数算法(Reference Counting)
比较简单,对每个对象保存一个整型 的引用计数器属性。用于记录对象被引用的情况。
对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加1;当引用失效时,引用计数器就减1。只要对象 A 的引用计数器的值为0,即表示对象A不可能再被使用,可进行回收。
优点:
- 实现简单方便,回收效率高
缺点:
- 需要单独的 字段存储计数器 ,这样的做法增加了存储空间的开销。
- 每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销。
- 引用计数器有一个严重的问题,即无法处理循环引用的情况。这是一 条致命缺陷,导致 在Java的垃圾回收器中没有使用这类算法(如下图)
2.2 可达性分析算法
这个算法的基本思路 就是通过 一系列称为 “GC Roots”
的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过 程所走过的路径称为 “引用链”(Reference Chain)
,如果某个对象到 GC Roots
间没有任何引用链相连, 或者用图论的话来说就是从 GC Roots
到这个对象不可达时,则证明此对象是不可能再被使用的。
在 Java
技术体系里面,固定可作为 GC Roots
的对象包括以下几种:
-
栈帧中的本地变量表中引用的对象。
-
静态变量。
-
字符串常量池里的引用。
-
在本地方法栈中
JNI(即通常所说的Native方法)
引用的对象。 -
Java
虚拟机内部的引用,如基本数据类型对应的Class对象
,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError
)等,还有系统类加载器。 -
所有被同步锁(synchronized关键字)持有的对象.
2.3 对象的finalization机制
- Java语言提供了对象终止
(finalization)
机制来允许开发人员提供对象被销毁之前的自定义处理逻辑。 - 当垃圾回收器发现没有引用指向一个对象,即:垃圾回收此对象之前,总会先调用这个对象的
finalize()
方法。 finalize()
方法允许在子类中被重写,用于在对象被回收时进行资源释放。通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等。- 永远不要主动调用某个对象的
finalize ()
方法,应该交给垃圾回收机制调用。理由包括下面三点:- ➢在
finalize()
时可能会导致对象复活。 - ➢
finalize()
方法的执行时间是没有保障的,它完全由GC线程决定,极端情况下,若不发生GC,则finalize()
方法将没有执行机会。 - ➢一个糟糕的
finalize ()
会严重影响GC的性能。
- ➢在
- 从功能上来说,finalize()方法与C++ 中的析构函数比较相似,但是Java采用的是基于垃圾回收器的自动内存管理机制,所以finalize()方法在本质,上不同于C++ 中的析构函数。
2.4 判断对象的死亡
即使在可达性分析算法中判定为不可达的对象,也不是 “非死不可”
的,这时候它们暂时还处于 “缓刑”
阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没 有与 GC Roots
相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是 否有必要执行 finalize()
方法。假如对象没有覆盖 finalize()
方法,或者 finalize()
方法已经被虚拟机调用过,那么虚拟机将这两种情况都视为 “没有必要执行”
。
如果对象重写了 finalize()
方法,且还没有执行该方法,则会把该对象插入到 F一Queue
队列中,由一个虚拟机自动创建的、低优先级的 Finalizer
线程触发其 finalize()
方法执行。
2.5 标记清除算法
概念:
如它的名字一样,算法分为 “标记”
和 “清除”
两个阶段:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,也可以反过来,标记存活的对象,统一回收所有未被标记的对象。
如图:
优点:
- 比较简单且经常使用。
缺点:
- 第一个是执行效率不稳定,如果
Java
堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低; - 第二个是内存空间的碎片化问题,标记、清除之后会产生大 量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找 到足够的连续内存而不得不提前触发另一次垃圾收集动作。
2.6 复制算法
为了解决 标记-清除算法
面对大量可回收对象时执行效率低的问题,1969年Fenichel提出了一种称为 “半区复制”(Semispace Copying)
的垃圾收集算法,它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
核心思想
将活着的内存空间分为两块,每次使用一块,进行垃圾回收的时候,将存活对象复制到另一块未使用的区域,然后将源区域清空,然后交换两个内存的角色
- 优点:
- 没有标记和清除过程,实现简单,运行高效
- 复制过去以后保证空间连续性,不会出现
“碎片”
问题。
- 缺点:
- 此算法的缺点也是很明显的,就是需要两倍的内存空间。
- 对于
G1
这种分拆成为大量region
的GC
,复制而不是移动,意味着GC
需要维护region
之间对象引用关系,不管是内存占用或者时间开销也不小。 - 特别的如果系统中的可用对象很多,复制算法不会很理想,因为要复制大量的对象。
在新生代,对常规应用的垃圾回收,一次通常可以回收70%一 99%的内存空间。回收性价比很高。所以现在的商业虚拟机都是用这种收集算法回收新生代。
2.7 标记压缩算法
背景:
复制算法的高效性是建立在存活对象少、垃圾对象多的前提下的。这种情况在新生代经常发生,但是在老年代,更常见的情况是大部分对象都是存活对象。如果依然使用复制算法,由于存活对象较多,复制的成本也将很高。因此,基于老年代垃圾回收的特性,需要使用其他的算法。 标记一清除算法的确可以应用在老年代中,但是该算法不仅执行效率低下,而且在执行完内存回收后还会产生内存碎片,所以JVM的设计者需要在此基础之上进行改进。标记一压缩(Mark一Compact) 算法由此诞生。 1970年前后,G. L. Steele 、C. J. Chene和D.S. Wise 等研究者发布标记一压缩算法。在许多现代的垃圾收集器中,人们都使用了标记一压缩算法或其改进版本。
核心思想:
- 第一阶段和标记一清除算法一样,从根节点开始标记所有被引用对象.
- 第二阶段将所有的存活对象压缩到内存的一端,按顺序排放。
- 之后,清理边界外所有的空间。
-
标记一压缩算法
的最终效果等同于标记一清除算法执行完成后,再进行一次内存碎片整理,因此,也可以把它称为标记一清除一压缩(Mark一 Sweep一Compact)算法。 -
二者的本质差异在于
标记清除算法
是一种 非移动式 的回收算法,标记压缩
是移动式的。是否移动回收后的存活对象是一项优缺点并存的风险决策。
清除算法的对比:
属性\算法 | 标记清除算法 | 复制算法 | 标记压缩算法 |
---|---|---|---|
时间复杂度 | 中 | 快 | 满 |
空间复杂度 | 少 | 占用2倍 | 少 |
内存碎片 | 有 | 无 | 无 |
移动对象 | 否 | 是 | 是 |
2.8 分代收集算法
前面所有这些算法中,并没有一种算法可以完全替代其他算法,它们都具有自己独特的优势和特点。分代收集算法应运而生。
分代收集算法,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。
一般是把 Java
堆分为 新生代
和 老年代
,这样就可以根据各个年代的特点使用不同的回收算法,以提高垃圾回收的效率。
目前几乎所有的GC都是采用分代收集(Generational Collecting) 算法执行垃圾回收的。
在HotSpot中,基于分代的概念,GC所使用的内存回收算法必须结合年轻代和老年代各自的特点。
- 年轻代(Young Gen)
- 年轻代特点:区域相对老年代较小,对象生命周期短、存活率低,回收频繁。
- 这种情况复制算法的回收整理,速度是最快的。复制算法的效率只和当前存活对象大小有关,因此很适用于年轻代的回收。而复制算法内存利用率不高的问题,通过
hotspot
中的两个survivor
的设计得到缓解。
- 老年代(Tenured Gen)
- 老年代特点:区域较大,对象生命周期长、存活率高,回收不及年轻代频繁。
- 这种情况存在大量存活率高的对象,复制算法明显变得不合适。一般是由标记清除或者是标记整理的混合实现。
- ➢标记阶段的开销与存活对象的数量成正比。
- ➢清除阶段的开销与所管理区域的大小成正相关。
- ➢压缩阶段的开销与存活对象的数据成正比。
3、内存溢出与内存泄露
3.1 内存溢出
内存溢出
内存溢出
相对于内存泄漏
来说,尽管更容易被理解,但是同样的,内存溢出
也是引发程序崩溃的罪魁祸首之一。- 由于
GC
一直在发展,所有一般情况下,除非应用程序占用的内存增长速度非常快,造成垃圾回收已经跟不上内存消耗的速度,否则不太容易出现OOM
的情况。 - 大多数情况下,
GC
会进行各种年龄段的垃圾回收,实在不行了就放大招,来一次独占式的Full GC
操作,这时候会回收大量的内存,供应用程序继续使用。 javadoc
中对OutOfMemoryError
的解释是,没有空闲内存,并且垃圾收集器也无法提供更多内存。
首先说没有空闲内存的情况:说明Java虚拟机的堆内存不够,原因有二:
- (1)
Java
虚拟机的堆内存设置不够。 比如:可能存在内存泄漏
问题;也很有可能就是堆的大小不合理,比如我们要处理比较可观的数据量,但是没有显式指定JVM
堆大小或者指定数值偏小。我们可以通过参数一Xms、一Xmx
来调整。 - 2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)对于老版本的
Oracle JDK
,因为永久代的大小是有限的,并且JVM
对永久代垃圾回收(如,常量池回收、卸载不再需要的类型)非常不积极,所以当我们不断添加新类型的时候,永久代出现OutOfMemoryError
也非常多见,尤其是在运行时存在大量动态类型生成的场合;类似intern
字符串缓存占用太多空间,也会导致OOM
问题。对应的异常信息,会标记出来和永久代相关:“java. lang. OutOfMemoryError: PermGen space”
。 随着元数据区的引入,方法区内存已经不再那么窘迫,所以相应的OOM有所改观,出现OOM,异常信息则变成了:“java. lang. OutOfMemoryError: Metaspace"
。 直接内存不足,也会导致OOM
。
这里面隐含着一层意思是,在抛出 OutOfMemoryError
之 前,通常垃圾收集器会被触发,尽其所能去清理出空间。
- ➢例如:在引用机制分析中,涉及到JVM会去尝试回收软引用指向的对象等。
- ➢在
java.nio.BIts.reserveMemory()
方法中,我们能清楚的看到,System.gc()
会被调用,以清理空间。
当然,也不是在任何情况下垃圾收集器都会被触发的。 - 比如,我们去分配一一个超大对象,类似一个超大数组超过堆的最大值,
JVM
可以判断出垃圾收集并不能解决这个问题,所以直接拋出OutOfMemoryError
3.2 内存泄漏(Memory Leak)
- 也称作
“存储渗漏”
。严格来说,只有对象不会再被程序用到了,但是GC
又不能回收他们的情况,才叫内存泄漏
。 - 但实际情况很多时候一些不太好的实践(或疏忽)会导致对象的生命周期变得很长甚至导致内存溢出
OOM
,也可以叫做宽泛意义上的内存泄漏
. - 尽管内存泄漏并不会立刻引起程序崩溃,但是一旦发生内存泄漏,程序中的可用内存就会被逐步蚕食,直至耗尽所有内存,最终出现
0utOfMemory
异常,导致程序崩溃。
注意,这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内存大小取决于磁盘交换区设定的大小。
举例
- 1、单例模式 单例的生命周期和应用程序是一样长的,所以单例程序中,如果持有对外部对象的引用的话,那么这个外部对象是不能被回收的,则会导致内存泄漏的产生。
- 2、一些提供
close
的资源未关闭导致内存泄漏 数据库连接( dataSourse. getConnection()),网络连接(socket)和io连接必须手动close,否则是不能被回收的。
4、垃圾回收器
- 1.优先调整堆的大小让JVM自适应完成。
- 2.如果内存小于100M,使用串行收集器
- 3.如果是单核、单机程序,并且没有停顿时间的要求,串行收集器
- 4.如果是多CPU、需要高吞吐量、允许停顿时间超过1秒,选择并行或者JVM自己选择
- 5.如果是多CPU、追求低停顿时间,需快速响应(比如延迟不能超过1秒,如互联网应用),使用并发收集器
- 官方推荐G1,性能高。现在互联网的项目,基本都是使用G1。
相关文章:

【学习笔记】深入理解JVM之垃圾回收机制
【学习笔记】深入理解JVM之垃圾回收机制 更多文章首发地址:地址 参考: 《深入理解JAVA虚拟机》第三版 第三章尚硅谷 第134 - 203 集参考文章:https://blog.csdn.net/qq_48435252/article/details/123697193 1、概念 🌻 首先我们…...

49.在ROS中实现local planner(2)- 实现Purepersuit(纯跟踪)算法
48.在ROS中实现local planner(1)- 实现一个可以用的模板实现了一个模板,接下来我们将实现一个简单的纯跟踪控制,也就是沿着固定的路径运动,全局规划已经规划出路径点,基于该路径输出相应的控制速度 1. Pur…...

Allegro如何设通孔Pin和Via的消盘操作指导
Allegro如何设通孔Pin和Via的消盘操作指导 用Allegro做PCB设计的时候,除了可以在光绘设置里面设置内层通孔Pin和Via的消盘,在设计过程中,同样也可以设置消盘效果,以便实时显示,如下图 如何设置,具体操作如下 点击Setup点击Unused Pads Suppression...

Android工厂模式
工厂模式分为三种 :简单工厂模式 、工厂方法模式 、抽象工厂模式 。 目录 简单工厂模式 UML图 实现 使用场景: 优点 : 缺点: 工厂方法模式 UML图 实现 使用场景: 优点: 缺点: 抽象工厂模式 UM…...

神经网络硬件加速器-架构篇
架构设计 常规架构通常包括两种: 1、全流水线架构,顾名思义,将整个神经网络进行平铺,并对每一层进行优化设计,优点:实现高吞吐率和低延时。缺点:消耗大量硬件资源,通常无法跨网络或…...

Python raise用法(超级详细,看了无师自通)
是否可以在程序的指定位置手动抛出一个异常?答案是肯定的,Python 允许我们在程序中手动设置异常,使用 raise 语句即可。 大家可能会感到疑惑,即我们从来都是想方设法地让程序正常运行,为什么还要手动设置异常呢&#…...

1.SpringSecurity快速入门
*SpringScurity的核心功能: 认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户 授权:经过认证后判断当前用户是否有权限进行某个操作 *第一步:创建springboot工程 *第二步:引入SpringSecurity依赖 *第三步:写controller,访问对应的url:localhos…...

Graph Partition: Edge cut and Vertex cut
Graph PartitionEdge cut and Vertex cutEdge cutVertex cut实际如何进行点分割和边分割的呢?Graph store format情况1:按照边列表存储:情况2:按照邻接表存储:Edge cut and Vertex cut 图结构描述了数据流动ÿ…...

Javascript周学习小结(初识,变量,数据类型)
JS的三大书写方式行内式如图所示:几点说明:JS的行内式写在HTML的标签内部,(常以on开头),如onclick行内式常常使用单引号括住字符串以区分HTML的双引号可读性差,不建议使用引号易出错,不建议使用特殊情况下使…...

C语言-基础了解-10-C函数
C函数 一、C函数 函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上&…...

【LeetCode】剑指 Offer(16)
目录 题目:剑指 Offer 33. 二叉搜索树的后序遍历序列 - 力扣(Leetcode) 题目的接口: 解题思路: 代码: 过啦!!! 写在最后: 题目:剑指 Offer …...

第三十九章 linux-并发解决方法二(互斥锁mutex)
第三十九章 linux-并发解决方法二(互斥锁mutex) 文章目录第三十九章 linux-并发解决方法二(互斥锁mutex)互斥锁的定义与初始化互斥锁的DOWN操作互斥锁的UP操作用count1的信号量实现的互斥方法还不是Linux下经典的用法,…...

脚本方式本地仓库jar包批量导入maven私服
脚本内容,将以下内容保存为mavenimport.sh,放置于需要上传的目录下,可以是顶层目录,或者某个分包的目录,若私服已有待上传的包,则执行会被替换 #!/bin/bash # copy and run this script to the root of th…...

【c++】引用的学习
引用的定义和声明 引用是一种别名,它允许使用与原变量相同的内存位置。在C中,引用是使用&符号来定义的。引用必须在定义时初始化,并且可以与原变量分别使用。 int a 10; int& b a; // 定义了一个引用b,它指向a引用的作用…...

linux 软件安装及卸载
1.联网在线安装及卸载ubuntu环境下:使用apt-get 工具apt-get install - 安装软件包apt-get remove - 移除(卸载)软件包CentOS环境下:使用yum工具 (银河麒麟系统属于centos)yum install - 安装软件包yum rem…...

XShell连接ubuntu20.04.LTS
1 下载XshellXShell官方下载地址打开XSHELL官方下载地址,我们可以选择【家庭和学校用户的免费许可证】,输入邮箱之后即可获得下载链接安装非常简单,跟着提示进行即可。2 连接ubuntu2.1 查看ubuntu的ip地址输入命令查看ip地址ifconfig刚开始可…...

【FPGA】Verilog:MSI/LSI 组合电路之解码器 | 多路分解器
写在前面:本章将理解编码器与解码器、多路复用器与多路分解器的概念,通过使用 Verilog 实现多样的解码器与多路分解器,通过 FPGA 并使用 Verilog 实现。 Ⅰ. 前置知识 0x00 解码器与编码器(Decoder / Encoder) 解码器…...

深入理解JDK动态代理原理,使用javassist动手写一个动态代理框架
文章目录一、动手实现一个动态代理框架1、初识javassist2、使用javassist实现一个动态代理框架二、JDK动态代理1、编码实现2、基本原理(1)getProxyClass0方法(2)总结写在后面一、动手实现一个动态代理框架 1、初识javassist Jav…...

一、策略模式的使用
1、策略模式定义: 策略模式(Strategy Pattern)定义了一组策略,分别在不同类中封装起来,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。比如我们要去某个地方,会根据距…...

Verilog使用always块实现时序逻辑
这篇文章将讨论 verilog 中一个重要的结构---- always 块(always block)。verilog 中可以实现的数字电路主要分为两类----组合逻辑电路和时序逻辑电路。与组合逻辑电路相反,时序电路电路使用时钟并一定需要触发器等存储元件。因此,…...

面向对象设计模式:行为型模式之迭代器模式
一、迭代器模式,Iterator Pattern aka:Cursor Pattern 1.1 Intent 意图 Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. 提供一种按顺序访问聚合对象的元素而不公开其基…...

如何快速在企业网盘中找到想要的文件
现在越来越多的企业采用企业网盘来存储文档和资料,而且现在市面上的企业网盘各种各样。在使用企业网盘过程中,很多用户会问到企业网盘中如何快速搜索文件的问题。但是无论是“标签”功能还是普通的“关键词搜索”功能,都是单层级的࿰…...

香橙派5使用NPU加速yolov5的实时视频推理(二)
三、将best.onnx转为RKNN格式 这一步就需要我们进入到Ubuntu20.04系统中了,我的Ubuntu系统中已经下载好了anaconda,使用anaconda的好处就是可以方便的安装一些库,而且还可以利用conda来配置虚拟环境,做到环境与环境之间相互独立。…...

算法练习-二分查找(一)
算法练习-二分查找 1 代码实现 1.1 非递归实现 public int bsearch(int[] a, int n, int value) {int low 0;int high n - 1;while (low < high) {int mid (low high) / 2;if (a[mid] value) {return mid;} else if (a[mid] < value) {low mid 1} else {high …...

通用业务平台设计(五):预警平台建设
前言 在上家公司,随着业务的不断拓展(从支持单个国家单个主体演变成支持多个国家多个主体),对预警的诉求越来越紧迫;如何保障业务的稳定性那?预警可以帮我们提前甄别风险,从而让我们可以在风险来临前将其消灭ÿ…...

Windows openssl-1.1.1d vs2017编译
工具: 1. perl(https://strawberryperl.com/) 2. nasm(https://nasm.us/) 3. openssl源码(https://www.openssl.org/) 可以自己去下载 或者我的网盘提供下载: 链接:…...

【深蓝学院】手写VIO第2章--IMU传感器--笔记
0. 内容 1. 旋转运动学 角速度的推导: 左ω∧\omega^{\wedge}ω∧,而ω\omegaω是在z轴方向运动,θ′[0,0,1]T\theta^{\prime}[0,0,1]^Tθ′[0,0,1]T 两边取模后得到结论: 线速度大小半径 * 角速度大小 其中,对旋转矩…...

网络基础(二)之HTTP与HTTPS
应用层 再谈 "协议" 协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢? 为什么要转换呢? 如果我们将struct message里面的信息…...

Python每日一练(20230306)
目录 1. 翻转二叉树 ★★ 2. 最长公共前缀 ★★ 3. 2的幂 ★ 1. 翻转二叉树 翻转一棵二叉树。 示例 1: 输入: 4/ \2 7/ \ / \ 1 3 6 9 输出: 4/ \7 2/ \ / \ 9 6 3 1示例 2: 输入: 1…...

C/C++每日一练(20230305)
目录 1. 整数分解 ☆ 2. 二叉树的最小深度 ★★ 3. 找x ★★ 1. 整数分解 输入一个正整数,将其按7进制位分解为各乘式的累加和。 示例 1: 输入:49 输出:497^2示例 2: 输入:720 输出:720…...