常见 JVM垃圾回收器、内存分配策略、JVM调优
一、哪些内存需要回收?
回答这个问题前需要先了解JVM到底有哪些内存区域,可参考:JVM运行时数据区域、内存溢出是怎样的?_jvm 运行时数据区哪些地方会产生内存溢出?-CSDN博客
程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭,栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的(尽管在运行期会由即时编译器进行一些优化,但在基于概念模型的讨论里,大体上可以认为是编译期可知的),因此这几个区域的内存分配和回收都具备确定性,在这几个区域内就不需要过多考虑如何回收的问题,当方法结束或者线程结束时,内存自然就跟随着回收了。
那还剩下什么区域?还有方法区和堆!
对于方法区,上面链接对应的文章中也说过:方法区的内存回收目标主要是针对常量池的回收和对类型的卸载,即:废弃的常量和不再使用的类型,但总的来说方法区的回收触发条件比较苛刻,发生垃圾回收的频率比较低。举个常量池中字面量回收的例子,假如一个字符串“java”曾经进入常量池中,但是当前系统又没有任何一个字符串对象的值是“java”,换句话说,已经没有任何字符串对象引用常量池中的“java”常量,且虚拟机中也没有其他地方引用这个字面量。如果在这时发生内存回收,而且垃圾收集器判断确有必要的话,这个“java”常量就将会被系统清理出常量池。常量池中其他类(接口)、方法、字段的符号引用也与此类似。
- 该类所有的实例都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例。
- 加载该类的类加载器已经被回收,这个条件除非是经过精心设计的可替换类加载器的场景,如OSGi、JSP的重加载等,否则通常是很难达成的。
- 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
垃圾回收的主要阵地是堆,为什么这么说?一个接口的多个实现类需要的内存可能会不一样,一个方法所执行的不同条件分支所需要的内存也可能不一样,只有处于运行期间,我们才能知道程序究竟会创建哪些对象,创建多少个对象,这部分内存的分配和回收是动态的。垃圾收集器所关注的正是这部分内存该如何管理。
二、垃圾收集第一步 —— 判断对象是死是活
2.1 引用计数法
public static void testGC(){GC objA = new GC();GC objB = new GC();objA.instance = objB;objB.instance = objA;objA = null;objB = null;System.gc();}
2.2 可达分析法
- 在虚拟机栈帧中的本地变量表引用的对象,如各线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
- 方法区中静态属性引用的对象,譬如Java类的引用类型静态变量。
- 方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用
- 本地方法栈中JNI(即通常所说的Native方法)引用的对象
- Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
- 所有被同步锁(synchronized关键字)持有的对象。
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
2.3 引用分类
- 强引用是最传统的“引用”的定义,即类似“Object obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。哪怕内存不足时,JVM也会直接抛出OutOfMemoryError。
- 软引用是用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存溢出异常。
- 弱引用也是用来描述那些非必须对象,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
- 虚引用也称为 “ 幽灵引用 ” 或者 “ 幻影引用 ”,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用 PhantomReference 类来表示,通过查看这个类的源码,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。
public class Main {public static void main(String[] args) {new Main().fun1();}public void fun1() {Object object = new Object();Object[] objArr = new Object[1000];}
}
当运行至Object[] objArr = new Object[1000];这句时,如果内存不足,JVM会抛出OOM错误也不会回收object指向的对象。不过要注意的是,当fun1运行完之后,object和objArr都已经不存在了,所以它们指向的对象都会被JVM回收。
如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。
public class Main {public static void main(String[] args) {SoftReference<String> sr = new SoftReference<String>(new String("hello"));System.out.println(sr.get());}
}
3.弱引用(WeakReference)
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:
public class Main {public static void main(String[] args) {WeakReference<String> sr = new WeakReference<String>(new String("hello"));System.out.println(sr.get());System.gc(); //通知JVM的gc进行垃圾回收System.out.println(sr.get());}
}
hello
null
第二个输出结果是null,这说明只要JVM进行垃圾回收,被弱引用关联的对象必定会被回收掉。不过要注意的是,这里说的被弱引用关联的对象是指只有弱引用与之关联,如果存在强引用同时与之关联,则垃圾回收时也不会回收该对象(软引用也是如此)。
public class Main {public static void main(String[] args) { ReferenceQueue<String> queue = new ReferenceQueue<String>();PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue); System.out.println(pr.get());}
}
null
要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
如何利用软引用和弱引用解决OOM问题?
2.4 其他说明
假如一个对象被可达分析法法判定为不可达对象,该对象是否就一定会被回收呢?答案是否定的。
三、垃圾收集算法
前面说了虚拟机怎么判定一个对象是死是活的基本理论,即引用计数法和可达性分析法。
垃圾收集算法基于这两种理论可以分为:引用计数式垃圾收集 和 追踪式垃圾收集 两种。但是主流的虚拟机一般都属于追踪式垃圾收集,因此此类型的垃圾收集器是重点。
3.1 分代收集理论
3.1.1 什么是分代?
- 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
- 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。另外请注意“Major GC”这个说法现在有点混淆,在不同资料上常有不同所指,需按上下文区分到底是指老年代的收集还是整堆收集。
- 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
- 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。
(新生代(Young)、老年代(Old)是HotSpot虚拟机,也是现在业界主流的命名方式)
3.1.2 跨代引用
3.1.3 分代的意义
3.2 标记--清除 算法
- 第一个是执行效率不稳定,如果Java堆中包含大量对象,而且其中大部分是需要被回收的,这时必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低;
- 内存空间的碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
3.3 标记--复制 算法
也叫复制算法,主要针对标记--清除算法面对大量可回收对象时执行效率的不足进行了改进。
标记--复制 算法将内存按容量划分为大小相等的两块,每次只使用其中的一块。当其中一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。
如果Java堆中包含大量对象,而且其中大部分是需要被回收的,标记--复制 算法只需要将少数的存活对象复制到另一块内存即可,内存复制开销很小,而且每次都是针对整个半区进行内存回收,分配内存时也就不用考虑有空间碎片的复杂情况,只要移动堆顶指针,按顺序分配即可。
所以 标记--复制 算法的优点是:1)面对大量要回收的对象时执行效率高;2)没有内存碎片产生。那它有没有缺陷呢?可定有啊,这种复制回收算法的代价是将可用内存缩小为了原来的一半,太浪费空间。其次,在面对堆中有大量的对象,且多数为不可回收的对象时,那复制的开销就会变大。所以 标记--复制 比较适用于年轻代的垃圾收集器采用,不适合老年代的垃圾收集器采用。
所以Serial、ParNewSerial、、Parallel Scavenge等年轻代垃圾回收期都是采用的标记-复制算法。
此外,还存在”内存担保“问题:这里是按1:1的比例划分内存的,但实际上,比如新生代中的对象有98%熬不过第一轮收集,那就没必要再按这个比例来划分内存,可以适当缩小保留区域的内存,因为毕竟存活下来的对象不多,不需要那么多内存来保存极少活下来的对象。
3.4 标记--整理 算法

四、经典垃圾回收器
4.1 CMS收集器
- 1)初始标记
- 2)并发标记
- 3)重新标记
- 4)并发清除
可以看出CMS中重点是标记,其中初始标记、重新标记这两个步骤仍然需要“Stop The World”,也就是需要暂停所有线程,那这样怎么还说CMS是响应快速的呢?那是因为初始标记仅仅只是标记一下GC Root能直接关联到的对象,速度很快;并发标记阶段就是从GC Root的直接关联对象开始遍历整个对象图的过程,这个过程虽然很耗时但却不需要停顿所有用户线程,用户线程可以和垃圾收集线程一起并发运行;重新标记阶段则是为了修正并发标记期间,因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间通常会比初始标记阶段稍长一些,但也远比并发标记阶段的时间短;最后是并发清除阶段,清理删除掉标记阶段判断的已经死亡的 对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的。
优缺点:
优点:并发收集、低停顿。
缺点:(1)CMS收集器对处理器资源非常敏感。如果应用本来的处理器负载就很高,还要分出一半的运算能力去执行收集器线程,就可能导致用户程序的执行速度忽然大幅降低。(2)CMS是一款基于“标记-清除”算法实现的收集器,收集结束时会有大量空间碎片产生。
4.2 Garbage First收集器 -- G1
- 1)初始标记:仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS指针的值,让下一阶段用户线程并发运行时,能正确地在可用的Region中分配新对象。这个阶段需要停顿线程,但耗时很短,而且是借用进行Minor GC的时候同步完成的,所以G1收集器在这个阶段实际并没有额外的停顿。
- 2)并发标记:从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象,这阶段耗时较长,但可与用户程序并发执行。对象图扫描完成以后,还要重新处理SATB记录下的在并发时有引用变动的对象。
- 3)最终标记:主要修正在并发标记阶段因为用户线程继续运行而导致标记记录产生变动的那一部分对象的标记记录。对用户线程做另一个短暂的暂停,用于处理并发阶段结束后仍遗留下来的最后那少量的SATB记录。
- 4)筛选回收:更新Region的统计数据,对各个Region的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划,可以自由选择任意多个Region构成回收集,然后把决定回收的那一部分Region的存活对象复制到空的Region中,再清理掉整个旧Region的全部空间。这里的操作涉及存活对象的移动,是必须暂停用户线程,由多条收集器线程并行完成的。
补充一、如何打印gc细节?打印的gc日志怎么看?
如何打印:
public class ReferenceCountingGC {public Object instance = null;private static final int _1MB = 1024*1024;private byte[] bigSize = new byte[2 * _1MB];public static void testGC(){ReferenceCountingGC objA = new ReferenceCountingGC();ReferenceCountingGC objB = new ReferenceCountingGC();objA.instance = objB;objB.instance = objA;objA = null;objB = null;System.gc();}public static void main(String[] args) {testGC();}
}

-XX:+PrintGCDetails (打印GC前后的详细信息)-XX:+PrintHeapAtGC(打印GC前后的详细堆栈信息)-Xloggc:filename (输出垃圾收集器的信息到一个指定的文件,例如:-Xloggc:F://log.txt )

串行收集器:
DefNew:是使用-XX:+UseSerialGC(新生代,老年代都使用串行回收收集器)。
并行收集器:
ParNew:是使用-XX:+UseParNewGC(新生代使用并行收集器,老年代使用串行回收收集器)或者-XX:+UseConcMarkSweepGC(新生代使用并行收集器,老年代使用CMS)。
PSYoungGen:是使用-XX:+UseParallelOldGC(新生代,老年代都使用并行回收收集器)或者-XX:+UseParallelGC(新生代使用并行回收收集器,老年代使用串行收集器)
garbage-first heap:是使用-XX:+UseG1GC(G1收集器)


补充二、如何使用JDK自带工具JConsole?


概述 :记录了“堆内存使用情况”、“线程”、“类”、“CPU使用情况”共四个资源的实时情况;
内存 :可以选择查看“堆内存使用情况”、“非堆内存使用情况”、“内存池"PS Eden Space"”等内存占用的实时情况;界面右下角还有图形化的堆一级、二级、三级缓存(从左到右)占用情况,当然,如果三级缓存被全部占用也就是很可能内存溢出啦!这时可以去查看服务器的tomcat日志,应该会有“outofmemory"的异常日志信息。界面右上角处还提供了一个“执行GC”的手动垃圾收集功能,这个也很实用~而且界面下方还有详细的GC信息记录。
线程 :界面上部显示实时线程数目。下部还能查看到详细的每个进程及相应状态、等待、堆栈追踪等信息;
类 :显示“已装入类的数目”、“已卸载类的数目”信息;
VM概要 :显示服务器详细资源信息,包括:线程、类、OS、内存等;
MBean : 可在此页进行参数的配置。
补充三、JVM调优经验
1、首先考虑GC策略,不合适的GC策略会影响效率。在jvm执行参数中选择合适的垃圾收集器;
2、其次考虑内存设置,虽然现在线上业务系统基本物理内存都是够用的,不过物尽其用,调优就是争取让每M空间都发挥出最大的作用。内存的设置还是最直观见效的。
-Xmx500m ,-Xms500m
最大堆内存和最小堆内存,这两个值要设的一致,避免虚拟机还要动态计算分配内存空间。
PS:堆也不是越大越好,堆太大带来的后果就是单次GC会较长。
-Xmn250m
新生代大小,非G1收集器可以设置这个值,G1的官方建议是不要显示分配新生代和老年代空间大小,因为G1会通过网格化内存来动态分配new/old区,官方认为不设置new size是最佳实践。
-Xss2m
每个线程的栈空间大小,默认值是1m,一般不需要设置,除非有递归方法存在可能会爆栈。
JDK8之后取消了永久代,改为元空间(MetaSpace),这块属于本地内存,理论上可以利用系统剩余的所有内存,不过跑了多个实例的话还是要设置一下为妙:
-XX:MetaspaceSize=128m,-XX:MaxMetaspaceSize=256m
-XX:MaxDirectMemorySize=128m
这个属于堆外内存,可以合理控制大小。Heap区总内存减去一个Survivor区的大小,不宜过大,否则可能
heap size + Direct Memory Size
把物理内存耗光。
-XX:SurvivorRatio=7
默认是8,新生代中Eden与Survivor的比值,过大的话可能Survivor存不下临时对象而频繁触发分配担保。可以根据GC日志看实际情况。
3、监控输出
监控参数还是需要的,线上偶尔OOM了时快速定位解决。
-XX:+HeapDumpOnOutOfMemoryError,-XX:HeapDumpPath={path}
OOM的时候会输出dump快照到{path}目录,只需要指向目录,文件名JVM会保持唯一性。
-XX:+PrintGCDetails,-Xloggc:logs/gc.log,-XX:+PrintGCTimeStamps,-XX:+PrintGCDateStamps
打印GC详细记录。
相关文章:

常见 JVM垃圾回收器、内存分配策略、JVM调优
垃圾收集( Garbage Collection ,下文简称 GC),垃圾收集的历史远远比 Java久远。经过半个世纪的发展,今天的内存动态分配与内存回收技术已经相当成熟,一切看起来都进入了“自动化”时代,那为什么…...

【HarmonyOS应用开发——ArkTS语言】欢迎界面(启动加载页)的实现【合集】
目录 😋环境配置:华为HarmonyOS开发者 📺演示效果: 📖实验步骤及方法: 一、在media文件夹中添加想要使用的图片素材 二、在entry/src/main/ets/page目录下创建Welcome.ets文件 1. 整体结构与组件声…...

【MySQL】:Linux 环境下 MySQL 使用全攻略
📃个人主页:island1314 🔥个人专栏:MySQL学习 ⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏 💞 💞 💞 1. 背景 🚀 世界上主…...

Linux驱动开发 gpio_get_value读取输出io的电平返回值一直为0的问题
当时gpio子系统进行读取时返回必定是0 因此,首先必须使用platform驱动来管理gpio和pinctrl子系统,然后如果按照正点原子所教的设备树引脚设置为0x10B0则会导致读取到的电平值为0。 解决方法: 将设备树中的引脚设置为 pinctrl_gpioled: gpio…...

【数据结构】栈与队列(FIFO)
在阅读该篇文章之前,可以先了解一下堆栈寄存器和栈帧的运作原理:<【操作系统】堆栈寄存器sp详解以及栈帧>。 栈(FILO) 特性: 栈区的存储遵循着先进后出的原则。 例子: 枪的弹夹,最先装进去的子弹最后射出来,最后装入的子弹…...
vue.js -ref和$refs获取dom和组件
在Vue.js中,ref和$refs是两个常用的属性,用于访问DOM元素和组件实例。下面分别详细解析这两个属性,并提供代码实例。 ref属性 ref属性用于给DOM元素或组件指定一个唯一的引用标识,在Vue实例中可以通过这个标识来访问对应的DOM元素…...

unity学习5:创建一个自己的3D项目
目录 1 在unity里创建1个3D项目 1.1 关于选择universal 3d,built-in render pipeline的区别 1.2 创建1个universal 3d项目 2 打开3D项目 2.1 准备操作面板:操作界面 layout,可以随意更换 2.2 先收集资源:打开 window的 AssetStore 下载…...

IEEE PDF eXpress遇到Font TimesNewRomanPSMT is not embedded的解决方案
IEEE PDF eXpress遇到Font TimesNewRomanPSMT is not embedded的解决方案 问题描述 在IEEE PDF eXpress上上传论文后,出现Font XXX is not embedded的问题。 该问题是指你所插入的图片等,没有将对应的字体嵌入进去。 解决方案 以下以Origin Lab图片…...

计算机网络 (21)网络层的几个重要概念
前言 计算机网络中的网络层是OSI(开放系统互连)模型中的第三层,也是TCP/IP模型中的第二层,它位于数据链路层和传输层之间,负责数据包从源主机到目的主机的路径选择和数据转发。 一、网络层的主要功能 路由选择…...

企业网络性能监控
什么是网络性能监控 网络性能监控(NPM)是指对计算机网络的性能进行持续测量、分析和管理的过程,通过监控流量、延迟、数据包丢失、带宽利用率和正常运行时间等关键指标,确保网络高效、安全地运行,并将停机时间降至最低…...
halcon三维点云数据处理(五)创建代表工具和机器人底座的3D模型
目录 一、gen_robot_tool_and_base_object_model_3d 函数调用二、gen_arrow_object_model_3d 函数调用 首先说明一下这部分代码在find_box_3d这个例程中,非常好用的一个坐标系生成函数。 一、gen_robot_tool_and_base_object_model_3d 函数调用 RobotToolSize : 0.…...

容器技术思想 Docker K8S
容器技术介绍 以Docker为代表的容器技术解决了程序部署运行方面的问题。在容器技术出现前,程序直接部署在物理服务器上,依赖管理复杂,包括各类运行依赖,且易变,多程序混合部署时还可能产生依赖冲突,给程序…...

25年1月更新。Windows 上搭建 Python 开发环境:PyCharm 安装全攻略(文中有安装包不用官网下载)
python环境没有安装的可以点击这里先安装好python环境,python环境安装教程 安装 PyCharm IDE 获取 PyCharm PyCharm 提供两种主要版本——社区版(免费)和专业版(付费)。对于初学者和个人开发者而言,社区…...
Oracle job(定时任务)
1、job的作用 可以定时执行任务(分/次、时/次、天/次等) 2、创建job --创建job --注意点: --①job_no 为系统自动获取; --②存储过程名需要加‘;’ --③定时器开始执行时间可以填‘sysdate,表示立即执行 --④执行频…...
[python3]Excel解析库-xlwt
xlwt 是一个用于创建 Excel .xls 文件(即旧版的 Excel 97-2003 格式)的 Python 库。它允许你用 Python 编写程序来生成 Excel 文件,而不需要实际运行 Microsoft Excel 应用程序。请注意,xlwt 只支持写入 .xls 文件,并不…...

【Rust自学】10.3. trait Pt.1:trait的定义、约束与实现
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 题外话:trait的概念非常非常非常重要!!!整个第10章全都是Rust的重难点!&#x…...

大数据高级ACP学习笔记(2)
钻取:变换维度的层次,改变粒度的大小 星型模型 雪花模型 MaxCompute DataHub...

K8s高可用集群之Kubernetes集群管理平台、命令补全工具、资源监控工具部署及常用命令
K8s高可用集群之Kubernetes管理平台、补全命令工具、资源监控工具部署及常用命令 1.Kuboard可视化管理平台2.kubectl命令tab补全工具3.MetricsServer资源监控工具4.Kubernetes常用命令 1.Kuboard可视化管理平台 可以选择安装k8s官网的管理平台;我这里是安装的其他开…...

【ArcGIS Pro二次开发实例教程】(2):BSM字段赋值
一、简介 一般的数据库要素或表格都有一个BSM字段,用来标识唯一值。 此工具要实现的功能是:按一定的规律(前缀中间的填充数字OBJECT码)来给BSM赋值。 主要技术要点包括: 1、ProWindow的创建,Label,Comb…...
OpenCV轮廓相关操作API (C++)
在OpenCV中,轮廓(contours)是图像处理中的一个重要概念,通常用于形状分析、物体检测等任务。OpenCV提供了多种与轮廓相关的API,可以在C中使用。 一.常用的与轮廓相关的操作及其对应的API函数 1.查找轮廓 findContou…...
极限复习c++
一、核心语法必背 1. 指针 vs 引用(简答题高频) 区别指针引用定义存储地址的变量,可改指向变量的别名,绑定后不可改初始化可空(nullptr)、延迟初始化必须初始化,不能引用空值访问需解引用&…...
使用 Ansible 在 Windows 服务器上安装 SSL 证书
在本教程中,我将向您展示如何使用 Ansible 在 Windows 服务器上安装 SSL 证书。使用 Ansible 自动化 SSL 证书安装过程可以提高 IT 运营的效率、一致性和协作性。我将介绍以下步骤: 将 SSL 证书文件复制到服务器将 PFX 证书导入指定的存储区获取导入的证…...
AI推荐系统演进史:从协同过滤到图神经网络与强化学习的融合
每一次滑动手机屏幕,电商平台向你推荐心仪商品的背后,是超过百亿量级的浮点运算。从早期的“猜你喜欢”到如今的“比你更懂你”,商品推荐引擎已悄然完成从简单规则到深度智能的技术跃迁。 一、协同过滤:推荐系统的基石与演进 协同…...
Java-IO流之压缩与解压缩流详解
Java-IO流之压缩与解压缩流详解 一、压缩与解压缩概述1.1 基本概念1.2 Java中的压缩类库1.3 核心类与接口 二、ZIP压缩与解压缩2.1 ZIP格式简介2.2 使用ZipOutputStream创建ZIP文件2.3 使用ZipInputStream读取ZIP文件 三、GZIP压缩与解压缩3.1 GZIP格式简介3.2 使用GZIPOutputS…...
5.2 HarmonyOS NEXT应用性能诊断与优化:工具链、启动速度与功耗管理实战
HarmonyOS NEXT应用性能诊断与优化:工具链、启动速度与功耗管理实战 在HarmonyOS NEXT的全场景生态中,应用性能直接影响用户体验。通过专业的性能分析工具链、针对性的启动速度优化,以及精细化的功耗管理,开发者能够构建"秒…...
Python应用break初解
大家好!作为 Python 初学者,控制循环的执行是编程中的基础技能之一。在本文中,我们将深入探讨break语句的用途和用法,帮助您更好地理解和掌握这一强大的工具。 定义: break是 Python 中的一个保留关键字,用于在循环中提前终止循环…...

【计算机网络】非阻塞IO——poll实现多路转接
🔥个人主页🔥:孤寂大仙V 🌈收录专栏🌈:计算机网络 🌹往期回顾🌹:【计算机网络】非阻塞IO——select实现多路转接 🔖流水不争,争的是滔滔不息 一、…...

COMSOL与MATLAB联合仿真人工智能的电学层析成像系统
关键词:MATLAB,电学层析成像,人工智能,图像重建,深度学习 一、引言 基于人工智能的电学层析成像系统是一种创新的检测技术,结合了电学层析成像技术与人工智能算法的优势。电学层析成像技术,简…...

web第九次课后作业--SpringBoot基于mybatis实现对数据库的操作
前言 在前面我们学习MySQL数据库时,都是利用图形化客户端工具(如:idea、datagrip),来操作数据库的。 在客户端工具中,编写增删改查的SQL语句,发给MySQL数据库管理系统,由数据库管理系统执行SQL语句并返回执…...
Vue中的自定义事件
一、前言 在 Vue 的组件化开发中,组件之间的数据通信是构建复杂应用的关键。而其中最常见、最推荐的方式之一就是通过 自定义事件(Custom Events) 来实现父子组件之间的交互。 本文将带你深入了解: Vue 中事件的基本概念如何在…...