当前位置: 首页 > news >正文

下一代的JDK - GraalVM

GraalVM是最近几年Java相关的新技术领域不多的亮点之一, 被称之为革命性的下一代JDK,那么它究竟有什么神奇之处,又为当前的Java开发带来了一些什么样的改变呢,让我们来详细了解下

下一代的JDK

官网对GraalVM的介绍是 “GraalVM 是一个高性能 JDK,可提高基于 Java 和 JVM 的应用的性能并简化 Java 云原生服务的构建和运行。它提供优化的编译器,可以更快地生成代码并降低计算资源消耗,实现微服务即时启动“

这里面有几个关键词:高性能云原生低资源即时启动。而这几个关键词其实恰恰也是当前Java应用开发面临的困境。大家都知道当前不管是企业服务还是互联网领域都在强调云原生开发,但云原生的一个核心要素就是需要资源和服务的快速调度,毕竟服务器和带宽都是要钱的,用户只想在使用服务时付费,而不是长时间让应用空转。比如目前流行的serverless模式,要求函数的生命周期尽可能的短,意味着频繁的启动和销毁,这对于服务的启动性能,资源消耗,服务包大小都有比较高的要求。但对于当前的JAVA应用来说却是无可承受的痛。所以为啥现在go,rust这些语言大行其道,其实就是迎合了云原生的需求场景。Java作为一个拥有20多年历史的语言,当然不可能坐以待毙,所以也在不断的探索怎么提升启动时间,减少资源消耗,GraalVM应该算是目前这些探索当中最靠谱,最成熟的一个。

如果GraalVM真的能解决以上的问题,那称其为革命性的下一代JDK或者说是云原生时代Java的希望之星也不为过吧。目前包括Spring Boot, Micronaut, Quarkus 这些知名框架也都对GraalVM提供了原生的支持。

那GraalVM究竟是如何做到这一切的呢?我们先来看一下GraalVM的架构图
在这里插入图片描述

从图上可以看出来,其实GraalVM核心是由两个部分构成的。其中之一是一个全新的名为Graal的编译器,它是完全用Java语言编写的,就是图中红色的部分,这也是GraalVM的名字的来源,可想而知这个编译器的重要性。

另外一个重要的组成部分就是用于构建高性能动态语言解释器的框架叫做 Truffle,就是最上面的这一层,它也是GraalVM能够支持多种编程语言运行在其上的基础框架,它现在支持的语言有很多呀,包括pyhton,js这些常见的脚本语言,借助于LLVM编译器,甚至可以让C或者C++都跑在GraalVM之上,这在以前是很难想象的。

最底层就是我们非常熟悉的 Hotspot JVM,这部分GraalVM 并没有对Hotspot JVM做大的调整,包括垃圾回收器,类加载器,线程管理,诊断和监控等特性都是直接复用Hotspot JVM的功能,这也保证了GraalVM能够与现有的JAVA生态保持完全的兼容,只是专注于编译优化和性能提升

Graal编译器

Graal实际上是一个用Java语言编写的JIT编译器。它的主要作用就是替换原本Hotspot JVM中用C++写的另外两个JIT编译器:C1和C2。C1主要是用client模式下,我们应该接触的比较少,C2是Hotspot JVM自带的高性能JIT编译器,当年Hotspot JVM出现时,正是凭借基于热点探测的JIT技术在性能上吊打其它的一众JVM,成为JAVA领域这么多年来事实的标准,它的名字(HOTSPOT)也正好反应了它的这个重要特性。

为啥还需要另一个JIT编译器

既然有了C2这样强大的JIT编译器,为啥还要重新开发一个呢?而且还是用Java语言来开发,Java的性能能比C++高吗?其实重写这个编译器还真不是为了优化性能,C2的性能已经非常好了,问题的关键在于C2是C++写的,而且还写的很复杂,年代也很久远,这让C2项目变得非常难以维护和扩展。

我们来看看来自C2编译器背后大佬Cliff Click的抱怨:

在这里插入图片描述
在这里插入图片描述

于是用JAVA这样一种更容易维护,安全性也要比C++高很多的语言来重写编译器也就不是 一件不可想象的事情了。至于性能,Graal毕竟比C2晚了快20年,后发优势是非常明显的,可以做到一些原来在C2中很难做到的编译优化,比如部分逃逸分析,经过测试目前Graal编译器的峰值性能已经追平甚至部分超越了C2。

另外,为了让类似Graal这样的编译器也能够有效的和底层的Hotspot VM配合工作,JAVA社区还提出了一个名为JVMCI(JAVA虚拟机编译器接口) 的提案,本质上就是将JIT编译器和底层的虚拟机解耦,提供更强的扩展性。

Java的自举

用Java编写Java的编译器,这也带来了一个里程碑式的变化,Java终于能够自举了!我们来撸一撸其中的过程:

在这里插入图片描述

首先,我们用java写的源代码会被javac工具编译成平台无关的字节码,这个javac工具就是完全用java语言实现的。然后字节码在运行时通过jvm的 解释器或者JIT 编译器编译成本地代码,这个解释器和JIT编译器也是完全用java语言编写的,这样就完成了闭环,这使得JAVA能够完全摆脱对其它语言的依赖,从而成为一门完全自主的语言。

静态编译

前面的Graal编译器虽然意义重大,但依然只是一个JIT编译器,Java应用的编译执行方式和原来也没有多大的区别,如果仅仅是这样那显然GraalVM也不会有这么大的影响力。我们再来看一个真正可称之为GraalVM的杀手锏的功能—静态编译

JIT与AOT

我们都知道,Java为了实现其平台无关性,首次编译(javac)的产出物其实是平台无关的字节码,这些字节码是无法直接被执行的。最早的Java Runtime都是通过JVM内置的解释器直接将字节码即时转换成本地代码来执行的,这样解释执行的方式肯定慢呀,所以当HOTSPOT JVM首次引入了JIT—即时编译或者叫做动态编译这样的概念以后,JAVA才真正确定了自己在业界的地位,也让HOTSPOT JVM一直作为官方虚拟机延续到了现在。

JIT(Just-in-Time) 简单的说,就是在运行时,把一部分热点代码,就是反复会调用的方法直接编译成本地代码,从而提升程序的运行效率。大家可能会有这样的疑问,为啥只编译热点代码呢,全部都编译成本地代码不是更快吗?因为编译很慢呀,开销也很大,如果编译消耗的资源都大于整个程序消耗的资源了,那么这么做就是本末倒置了。所以JIT就是一种折中的做法,把频繁运行的方法编译成本地代码提升执行效率,其它偶尔才运行的代码还是通过解释器去执行,这样能够最大程度的平衡启动时间,资源消耗和性能。

– 与之相对的就是静态编译,也叫做AOT(Ahead-of-Time) 编译。这个名词最早被大家听说应该是在android领域,在安卓5.0版本,当时谷歌引入了ART的新的运行时环境,并推出了AOT编译模式,故名思意AOT编译是在应用安装时一次性把字节码编译成机器码,而不是像JIT那样切香肠式的编译,这样能大幅提升应用的启动速度。但早期的AOT编译会造成应用安装时间过长,空间占用较多的问题,后来谷歌又改成AOT和JIT混合编译的策略了。说回JAVA哈,安卓和JAVA本就是同源的,安卓可以AOT编译,JAVA当然也可以,最早在JAVA 9.0 , 就引入了这方面的概念和支持,这个版本有一个AOT编译器叫做jaotc(对应原来的javac),但因为各种原因一直没得到多少重视。

我们可以通过一张图来比较一下两种编译方式各自的特点:

在这里插入图片描述

一般来说,AOT在启动速度,内存占用,包体积等方面占优势,但编译过程较慢;而JIT编译很快,二期因为可以基于运行时的信息做更激进的编译优化,因此在峰值吞吐量和响应延迟这些方面可能会更有优势。

Native Image

AOT和JIT应该说是各有所长,不存在谁取代谁的问题。但在云原生,微服务架构大行其道的当下,大家更看重的还是应用的启动速度和资源占用等特点,所以静态编译又变成了一个非常有诱惑力的选型。GraalVM也提供了自己的java静态编译工具,也就是 Native Image 。这个工具可以直接将Java应用程序编译成可本地执行的文件,脱离虚拟机直接运行。它背后需要解决三方面的问题:

编什么

源代码肯定要编译,但是依赖呢?是不是所有依赖都要编译。理论上所有依赖都编译肯定是没有问题的,而且后面反射的问题也顺带就解决了。但现实场景这基本不可能,我们现在的应用程序依赖链一般都很深,如果把classpath上能扫描到的jar包都给编译了,那估计真的要等到天荒地老了。所以这一步是走不通的。

Native Image采用的方法是构建一个从应用程序的入口函数,也就是main方法开始,到所有可达代码的这样一个调用图。这个调用图可以认为是应用程序在实际执行时所有可能的执行路径之和,只要构造了这样的图,也就知道了哪些方法最终是可能被执行的,这些方法也就需要被编译。

在这里插入图片描述

如何适配JVM运行时

JVM运行时可不单是提供JIT的支持,垃圾回收,反射,动态代理这些所谓的运行时特性也需要在编译成静态本地代码时解决。这个的解决方案有点简单粗暴,就是直接在编译后的执行文件中塞一个精简后的JVM— SVM(Substrate VM) ,它包括了一组轻量级的运行时库和基础设施,提供包括内存管理、垃圾回收、线程管理等一系列基础特性。

除了这些,还要解决反射调用,JNI调用这类动态调用的问题。刚才也提到过,Native Image编译的时候会做静态分析,从主函数入口开始遍历所有的可达的方法作为整个编译范围,但是这样的扫描有一个缺陷,就是无法确定那些只有在运行时才会获取到的动态调用信息,也就不会将动态调用的目标加入到编译范围,这样在运行时肯定就报错了。而且现在诸如spring这样的框架,大量使用了反射调用,如果置之不理,那么常规的应用直接编译肯定都无法正常使用的。

Native Image的解决方式是,开发者主动以配置文件的方式告诉编译器哪些方法是动态调用,这样编译器就能提前将其加入到编译范围中,避免运行时出现问题。当然这个配置文件手搓肯定不现实,就算自己写的代码可以统计,那么第三方库里面这么多动态调用也是无法统计的。所以还有一个配套的小工具:native-image-agent,应用程序可以先以jar包的方式进行预执行,并配置这个代理。这个代理程序会收集应用程序在执行期间所触发的所有动态调用信息,并自动生成配置文件。

$JAVA_HOME/bin/java -agentlib:native-image-agent=config-output-dir=/path/to/config-dir/,config-write-period-secs=300,config-write-initial-delay-secs=5 ...

执行之后就会生成类似下面这样的配置文件:

在这里插入图片描述

有了这个配置文件以后,Native Image就会在编译时将反射的目标对象也加入到编译范围当中,并同时填充ReflectionData结构中,这个ReflectionData可以简单理解为反射对象的数据缓存,如果是在JVM中执行反射,第一次会先执行一次JNI调用,通过本地方法获取反射目标,再加入到ReflectionData缓存中,后续调用直接从缓存中读取目标对象信息,所以首次调用的开销要远高于后续的调用。而SVM在静态编译时就可以根据配置信息填充这个ReflectionData,这样反射调用就会比在JVM中要高效的多,这也是静态编译的一个优势。最后就是执行阶段,应用只需要简单的查询ReflectionData是否有相关目标信息,没有就直接报错,有才会调用。 整个反射的实现原理大概就是这样:

在这里插入图片描述
在这里插入图片描述

怎么编

最后再来看一下如何进行编译。编译器其实还是之前提到过的那个Graal编译器,但静态编译和JIT编译相比还是存在一些局限性,影响最大的就是无法获取运行时信息进行优化,比如如果基于运行时数据,JIT编译器发现某个IF判断几乎只执行其中一个分支,而另一个分支很少执行,那么JIT编译器在生成机器指令的时候就可以在IF判断前就准备好其中高频分支的相关数据,让后续代码能够快速执行,这就是所谓的分支预测。但静态编译因为不存在运行时数据,所以也就做不了相应的优化了,这里我列了一些静态编译和动态编译在编译期优化时的区别:

在这里插入图片描述

总结

GraalVM提供了Java应用程序运行的另一种方式,这将带来深远的影响,在云原生时代JAVA语言也终于有了自己的一席之地了。它主要有以下几个方面的好处:

  • 即时启动: 这个效果非常明显,基本就是所谓的秒起,和原来动辄等待十几二十秒简直是天壤之别。

  • 节约内存 : 经过实测空载内存差不多能够减少到原来的五分之一左右,虽然仍然比同一规模的go,rust等应用占用内存要多,但已经很惊艳了。

  • 安全性 : 这个可能是很多同学没想到的,这要从两个方面来分析:首先是静态编译仅编译实际应用需要的类和方法,这也意味着你即使引用了一个可能有安全漏洞的包,只要你实际运行的代码里面没有调用它有漏洞的方法,那就不会被攻击,相当于减少了被攻击的几率。另外Java之前反编译的风险是非常大的,因为编译产物是与源码结构类似的字节码,除非用一些比较复杂的方式来做编译期混淆,例如我们现在用的三方混淆插件,有多难用大家应该深有体会了。而静态编译后生成的是本地代码,不说完全解决了反编译的风险,至少安全性也是大大的提升了

但是静态编译如果要大规模应用到生产环境,目前还是存在一些局限性的,主要有下面一些:

  • 编译繁琐 GraalVM支持反射需要依赖于独立的配置,目前虽然可以通过native-image-agent的方式来自动生成配置文件,但这个依赖于预执行的路径覆盖,如果不能在预执行阶段做到覆盖主要的执行分支,那么有可能生成出来的反射配置文件也是不完整的。这个建议如果要在产品中使用的话,一定要为代码编写对应的单元测试用例,并尽可能覆盖到主要的业务分支,便于agent收集运行时数据进行编译。

  • 调试工具的缺乏 编译成可执行文件之后,原来JVM配套的那些调试和监控工具可能就用不了了,包括我们熟知的jstack,jmap,不过Graalvm也提供了一些替代的方式帮助我们对native应用进行观测和调试,这部分大家可以下来自己看一下,这里就不展开了

  • GC策略支持有限 目前版本的GraalVM仅支持Serial GC和G1两种垃圾回收器,而且社区版本还不支持G1,只有企业版能用。Serial GC就是串行回收,是Java比较早期的垃圾回收策略,虽然GraalVM中对其进行了优化,但是也仅推荐用于堆比较小的应用程序,不太适合大型应用。这个只能寄希望于Oracle后续能够将G1也下放给社区版了,大家显然也不太可能去购买企业版本。不过好消息是Oracle前不久刚刚出了一个GraalVM的闭源免费版本(Oracle GraalVM 区别于开源版本的 GraalVM CE ),特性完全和企业版是一样的,免费使用,只是不开源,有兴趣的同学可以去研究下它的license。

在这里插入图片描述
欢迎关注我的公号—飞空之羽的技术手札,有深度的技术好文~

相关文章:

下一代的JDK - GraalVM

GraalVM是最近几年Java相关的新技术领域不多的亮点之一, 被称之为革命性的下一代JDK,那么它究竟有什么神奇之处,又为当前的Java开发带来了一些什么样的改变呢,让我们来详细了解下 下一代的JDK 官网对GraalVM的介绍是 “GraalVM 是…...

Java三方库-单元测试

文章目录 Junit注解常用类无参数单测带参数的单测 Junit 主要版本有4和5版本,注解不太一样, 4迁移5参考官方文档 主要记录下常用的一些操作 其他复杂操作见官网 https://junit.org/junit5/docs/current/user-guide/#overview-java-versions 引入5.9…...

p2p、分布式,区块链笔记: libp2p基础

通信密钥 noise::{Keypair, X25519Spec} X25519/Ed25519类似RSA 算法。Noise 用于设计和实现安全通信协议。它允许通信双方在没有预先共享密钥的情况下进行安全的密钥交换,并通过加密和身份验证保护通信内容。libp2p 提供了对 Noise 协议的原生支持,它允…...

企业本地大模型用Ollama+Open WebUI+Stable Diffusion可视化问答及画图

最近在尝试搭建公司内部用户的大模型,可视化回答,并让它能画图出来, 主要包括四块: Ollama 管理和下载各个模型的工具Open WebUI 友好的对话界面Stable Diffusion 绘图工具Docker 部署在容器里,提高效率以上运行环境Win10, Ollama,SD直接装在windows10下, 然后安装Docker…...

Unity学习笔记---调试

使用Log进行调试 使用Debug.Log方法可以将一些运行时信息打印到Console窗口中。 打印时间戳 //获取时间 Debug.Log(DateTime.Now.ToString());//打印毫秒级的时间 Debug.Log(((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000) * 0.001); 打印自定义文…...

Py之dashscope:dashscope的简介、安装和使用方法、案例应用之详细攻略

Py之dashscope:dashscope的简介、安装和使用方法、案例应用之详细攻略 目录 dashscope的简介 1、产品的主要特点和优势包括: dashscope的安装和使用方法 1、安装 2、使用方法 dashscope的案例应用 1、通义千问-Max:通义千问2.5系列 2…...

Go使用Gin框架开发的Web程序部署在Linux时,无法绑定监听Ipv4端口

最近有写一部分go语言开发的程序,在部署程序时发现,程序在启动后并没有绑定ipv4的端口,而是直接监听绑定ipv6的端口。 当我用netstat -antup | grep 3601查找我的gin服务启动的端口占用情况的时候发现,我的服务直接绑定了tcp6 &a…...

【图解大数据技术】Hadoop、HDFS、MapReduce、Yarn

【图解大数据技术】Hadoop、HDFS、MapReduce、Yarn HadoopHDFSHDFS架构写文件流程读文件流程 MapReduceMapReduce简介MapReduce整体流程 Yarn Hadoop Hadoop是Apache开源的分布式大数据存储与计算框架,由HDFS、MapReduce、Yarn三部分组成。广义上的Hadoop其实是指H…...

AGPT•intelligence:带你领略全新量化交易的风采

随着金融科技的快速发展,量化交易已经成为了投资领域的热门话题。越来越多的投资者开始关注和使用量化交易软件来进行投资决策。在市场上有许多量化交易软件可供选择。 Delaek,是一位资深的金融科技专家,在 2020年成立一家专注于数字资产量化…...

HarmonyOS Next开发学习手册——创建轮播 (Swiper)

Swiper 组件提供滑动轮播显示的能力。Swiper本身是一个容器组件,当设置了多个子组件后,可以对这些子组件进行轮播显示。通常,在一些应用首页显示推荐的内容时,需要用到轮播显示的能力。 针对复杂页面场景,可以使用 Sw…...

【计算机视觉】mmcv库详细介绍

文章目录 MMVC库概览特点和优势主要组件应用案例示例一:数据加载和处理示例二:模型训练和验证MMVC库概览 MMCV 是一个用于计算机视觉研究的开源库,它为各种视觉任务提供了底层的、高度优化的 API。该库涵盖了从数据加载到模型训练的各个方面,广泛应用于开源项目,如 MMDet…...

【面试系列】Go 语言高频面试题

欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、…...

React 扩展

文章目录 PureComponent1. 使用 React.Component,不会进行浅比较2. 使用 shouldComponentUpdate 生命周期钩子,手动比较3. 使用 React.PureComponent,自动进行浅比较 Render Props1. 使用 Children props(通过组件标签体传入结构&…...

IT入门知识第八部分《云计算》(8/10)

目录 云计算:现代技术的新篇章 1. 云计算基础 1.1 云计算的起源和发展 云计算的早期概念 云计算的发展历程 1.2 云计算的核心特点 按需自助服务 广泛的网络访问 资源池化 快速弹性 按使用量付费 1.3 云计算的优势和挑战 成本效益 灵活性和可扩展性 维…...

Linux-笔记 全志T113移植正点4.3寸RGB屏幕笔记

目录 前言 线序整理 软件 显示调试 触摸调试 背光调试 前言 由于手头有一块4.3寸的RGB屏幕(触摸IC为GT1151),正好开发板上也有40Pin的RGB接口,就想着给移植一下,前期准备工作主要是整理好线序,然后用转接板与杜邦线连接验证好…...

Linux shell编程学习笔记59: ps 获取系统进程信息,类似于Windows系统中的tasklist 命令

0 前言 系统进程信息是电脑网络信息安全检查中的一块重要内容,对于使用Linux和基于Linux作为操作系统的电脑来说,可以使用ps命令。 1 ps命令 的功能、格式和选项说明 1.1 ps命令 的功能 Linux 中的ps(意为:process status&…...

在Android中使用ProgressBar显示进度

在Android中使用ProgressBar显示进度 大家好,我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编,也是冬天不穿秋裤,天冷也要风度的程序猿!今天我们将探讨如何在Android应用中使用ProgressBar来显示进度。ProgressB…...

Java基础面试题(简单版):

1.java的8个基本数据类型? 整型: byte(占用1个字节) short(占用2个字节) int(占用4个字节) long(占用8个字节) 浮点型: float(占用4个字节)、double(占用8个字节) 字符型: char 布尔型: boolean 2.ArrayList和LinkedList的区别? 可以说ArrayList和LinkedList除了是同属于集合…...

​Chrome插件:Postman Interceptor 调试的终极利器

今天给大家介绍一款非常实用的工具——Postman Interceptor。 这个工具可以捕捉任何网站的请求,并将其发送到Postman客户端。 对于经常和API打交道的程序员来说,Postman Interceptor真的是神器级别的存在。 下面就让我详细说说这个插件怎么用&#xf…...

SpringBoot学习04-[定制SpringMVC]

定制SpringMVC 定制SpringMvc的自动配置定制springmvc-configurePathMatch配置定制SpringMVC-拦截器Interceptor定制SpringMVC-CORS配置全局cors配置针对某个方法加跨域解决 WebMvcConfigurer原理定制SpringMVC-JSONJSON开发jackson的使用定制化json序列化和反序列化 JSON国际化…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器

一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

如何为服务器生成TLS证书

TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...

C++使用 new 来创建动态数组

问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

C++ 设计模式 《小明的奶茶加料风波》

👨‍🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...