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

【Java EE初阶三十】JVM的简单学习

1. JVM 内存区域划分

        
        一个运行起来的 Java 进程,就是一个 JVM 虚拟机,需要从操作系统申请一大块内存,就会把这个内存,划分成不同的区域,每个区域都有不同的作用.
        JVM 申请了一大块内存之后,也会划分成不同的内存区域,下面来详细讲解一下各个区域:

1、方法区

        (jdk1.7 及其之前)/ 元数据区 (jdk1.8 开始)
        这里存储的内容,就是类对象。.class 文件加载到内存之后,就成了类对象。

2、堆
        这里存储的内容,就是代码中 new 的对象(该区域是占据空间最大的区域)

3、栈
        这里存储的内容,就是代码执行过程中,方法之间的调用关系,如下图所示:

        4. 程序计数器
        是一个比较小的空间,主要就是存放一个"地址",表示下一条要执行的指令在内存中的哪个地方(方法区里每个方法里面的指令, 都是以二进制的形式保存到对应的类对象中的),刚开始调用方法,程序计数器记录的就是方法的入口的地址,随着一条一条的执行指令,每执行一条,程序计数器的值都会自动更新,即去指向下一条指令;

        如果是一个顺序执行的代码,下一条指令就是把指令地址进行递增;

        如果是条件/循环代码,下一条指令就可能会跳转到一个比较远的地址;

        下面进行举例:

主要知识点如下图所示:

        本地方法:指的是使用 native 关键字修饰的方法.这个方法不是使用 java 实现,而是在jvm 内部通过 C++ 代码实现的,JVM 内部的 C++ 代码调用关系;

        虚拟机栈,以及程序计数器都是每个线程都有一份,JVM 进程中有 10 个线程就会有 10 个虚拟机栈,也会有 10 个程序计数器(每个线程各自有一个)

        堆区和元数据区,这俩东西在 JvM 进程中是只有一份的

        下面给你一个代码,问你某个变量是处于内存中的哪个区域中?

        一个变量处于哪个区域,和变量的形态密切相关,局部变量处于栈上。成员变量在堆上,静态变量在与数据区内;同时我们要知道变量处于哪个空间上,和变量是不是引用类型,是不是基本类型是没有关系的

        new 出来的对象在堆上,于此同时又创建了一个局部变量 Test t引用类型的变量,就把对象地址存储到t里面了,t里面存储的不是对象本身;

        一个 JvM 进程里,可能有多个线程.每个线程,有自己的程序计数器和栈空间,这些线程共用同一份堆和方法区;

2. jvm中类加载的过程

        java 代码会被编译成.class 文件(包含了一些字节码),java 程序要想运行起来,就需要让 jvm读取到这些.class 文件,并且把里面的内容,构造成类对象并保存到内存的方法区中.(所谓的"执行代码”,就是调用方法.就需要先知道每个方法编译后生成的指令都是啥)

        一般来说将整个类加载的过程分为5个部分:

2.1 加载

        找到 .class 文件,打开文件,读取文件内容;

        往往代码中,会给定某个类的,"全限定类名“,例如java.lang.String,java.util.ArrayList,jvm 就会根据这个类名,在一些指定的目录范围内查找.

2.2 .验证

        .class 文件是一个二进制的格式,(每一个字节,都是有某些特定含义的)就需要验证你当前读到的这个格式是否符合要求.

        下图就是.class文件需要遵循的格式:

2.3 准备 

        给类对象分配内存空间 (最终的目标就是要构造出 类对象)

        我们这一步只是分配内存空间,还没有初始化呢.此时这个空间上的内存的数值,就是全0 ,(此时如果尝试打印类的 static 成员,结果就是全0的);

2.4 解析

         针对类对象中包含的字符串常量进行处理, 进行一些初始化操作,java 代码中用到的字符串常量在编译之后,也会进入到 .class 文件中;下面通过对字符串s的解析进行进一步说明:

        于此同时,.class 文件的二进制指令中,也会有一个s这样的引用被创建出来,由于引用里面本质上保存的是一个变量的地址,在 .class 文件中,这是文件不涉及到内存地址,因此住 .class 文件中,s 的初始化语句,即先会被设置成一个“文件偏移量”,通过偏移量,就能找到"test"这个字符串所在的位置;

        下面来简单说明一下文件偏移量:

        接下来,

这个过程,也叫做: 把"符号引用(文件偏移量)替换成“"直接引用)(内存地址)

2.5 初始化

        针对类对象进行初始化

        把类对象中需要的各个属性都设置好,同时还需要初始化好 static 成员变量,还需要执行静态代码块,以及还可能需要加载一下父类.

2.6 双亲委派模型

        双亲委派模型属于类加载中第一个步骤->"加载"过程中的一个环节,主要是根据负责根据全限定类名来找到 .class 文件.

        类加载器是 JVM 中的一个模块,JM 中中内置了下面如图所示的三个类加载器:

        如上图所示,这个父子关系并不是“继承"构成的,而是这几个 ClassLoader 里有一个 parent 这样的属性,指向了一个 父"类加载器";

        下面来详细描述一下类加载的过程(即找 .class 文件的过程):

1、给定一个类的全限定类名,形如 java.lang.String;

2、从 Application ClassLoader 作为入口,开始执行査找的逻辑

3、Application ClassLoader不会立即去扫描自己负责的目录(负责的是搜索项目当前目录和第三方库对应目录),而是把查找的任务,交给它的父亲,Extension ClassLoade

4、 Extension ClassLoader,也不会立即扫描自己负责的目录(负责的是 JDK 中一些扩展的库所对应的目录),而是把查找的任务交给它的父亲BootStrap ClassLoade;

5、Bootstrap ClassLoader,也不想立即扫描自己负责的目录(负责的是 标准库 的目录),也想把任务交给它的父亲,结果发现,自己没有父亲,因此 BootStrap ClassLoader 只能亲自负责扫描标准库的目录,如下所示:

        像java.lang.String.这种类,就能够在标准库中,找到对应的 .class 文件,就可以进行打开文件,读取文件......此时,查找 .class 文件的过程就结束了;但是,如果给定的类不是标准库的类,任务仍然会被交给孩子来执行;

6、没有扫描到就会回到 Extension ClassLoader.Extension ClassLoader 就会扫描负责的扩展库的目录.如果找到,就执行后续的类加载操作,此时查找过程结束还没有没找到,就会把任务交给他的孩子来执行.

7、没有扫描到, 就会回到 Application ClassLoade),Application ClassLoader 就会负责扫描当前项目和第三方库的目录.如果找到,就执行后续的类加载操作.如果没找到,就会抛出一个 ClassNotFoundException;

        综上所述,所谓的“双亲委派模型“,其实就是一个查找优先级的问题;简单的查找流程如下图所示:

        之所以搞这一套流程,主要的目的是为了确保标准库的类被加载的优先级最高,其次是扩展库,其次是自己写的类和第三方库;

3. GC 垃圾回收 

        C 语言中,像 malloc 这种, 就属于是"动态申请内存”(运行时),其实很多时候, 需要程序真正跑起来,才能确定内存的大小;

        在C 语言中, 使用 malloc 申请的内存,需要在用完之后, 通过 free 来释放。(此处如果不释放,就会产生"内存泄露”这样的问题)

        C++里,动态申请内存,变成 new,malloc 只是申请内存.new,能够申请内存,也能初始化(调用构造函数),在C++ 中,使用 new 申请的内存,用完之后, 通过 delete 来释放;

        在 Java 中, new 一个对象也就是"动态内存申请”;

        综上所述,相比之下, java 给出了一个方案,垃圾回收机制 (GC),让 JVM 自行判定,某个内存是否就不再使用了,如果这个内存后面确实不用了,JVM 就自动的把这个内存给回收掉,此时就不必让程序猿自己手动写代码回收;

        下面首先来了解一下GC机制的缺陷:

        1.系统开销,需要有一个或一些特定的线程,不停的扫描内存中的所有的对象, 看是否能够回收,此时是需要额外的内存和CPU 资源的.

        2.效率问题,这样的扫描线程,不一定能够及时的释放内存(扫描总是有一定周期的)一旦同一时刻,出现大量的对象都需要被回收,GC 产生的负担就会很大.甚至引起整个程序都卡顿.(STW 问题:即stop the world);

        GC 是垃圾回收,GC 回收的目标其实是内存中的对象,对于 Java 来说, 就是 new 出来的这些对象;栈里的局部变量,是跟随着栈帧的生命周期走的,(方法执行结束,栈帧销毁,该部分的内存自然释放);静态变量的生命周期就是整个程序,这个始终存在就意味着静态变量是无需释放的;因此真正需要 gc 释放的, 就是堆上的对象了.

        gc 可以理解成两个大的步骤:
1. 找到垃圾
2. 释放垃圾

3.1  找到垃圾

        在 GC 的领域中, 有两种主流的方案:

1)、引用计数 [Python, PHP]

        new 出来的对象,单独安排一块空间,来保存一个计数器,如下图所示:

        在Java 中,使用对象,必须要依靠引用,如果一个对象,没有引用指向了,就可以视为是垃圾了(引用计数就是0 )

        对于上述代码,出了 { }之后,t 和 t2 就都销毁了,即引用计数就要归0了,当对象的引用计数为 0,此时代码中就不可能访问到这个对象了,此时这个对象就可以视为是垃圾了;

        关于 java 不使用引用计数的分析,引用计数存在两个重要的问题:

        1、比较浪费内存.
        计数器最少需要2个字节,如果对象本身就很小,这个计数器占据的空间比例就很大;
比如对象本身就 2 个字节,计数器占据的空间就是 50%;如果对象本身 4个字节?计数器占据的空间就是 33% ;如果对象很少, 或者对象比较大, 都影响不大。但是如果对象小并且很多,计数器所占据的空间就十分巨大;

        2、引用计数机制,存在"循环引用”问题,如下面的一段代码所示:

 a,b及其引用的内存分布如下所示:

        此时,当前a和 b 两个引用已经销毁了,new 出来的这俩对象,已经无法被其他代码访问到了,但是他们的引用计数却是不为0的,所以这俩对象是不能被回收的;此时,第一个对象引用了第二个对象,第二个对象引用了第一个对象.

2)、可达性分析 【java】
        可达性分析本质上是时间换空间的手段;

        有一个/一组线程周期性的扫描我们代码中所有的对象,从一些特定的对象出发,尽可能的进行访问的遍历,把所有能够访问到的对象,都标记成“可达”,反之,经过扫描之后, 未被标记的对象,就是垃圾;

        不仅仅是所有的局部变量,还有常量池中引用的对象,还有方法区中的静态引用类型引用的变量,都统称为 GCRoots,当然,这里的遍历大概率是 N 叉树.主要就是看所访问的某个对象,里面有多少个引用类型的成员,并针对每个引用类型的成员都需要进一步的进行遍历;

        可达性分析都是周期性进行的,当前某个对象是否是垃圾,是随着代码的执行而发生改变,总之就是可达性分析比较消耗系统资源,开销比较大;

3.2 回收垃圾

3.2.1 标记清除

        该方式是比较简单粗暴的释放方式,下面黑色区域是被标记的要被清除的,

        把对应的对象,直接释放掉,就是标记清除的方案,但是这个方案其实非常不好,因为会产生很多的内存碎片,释放内存的主要目的是为了让别的代码能够申请到"连续”的内存空间,但是这样会导致我们能用的内存是断断续续的;随着时间的推移,内存碎片的情况就会越演越烈,如此就会导致后续内存申请举步维艰.

3.2.2 复制算法

        通过复制的方式,把有效的对象归类到一起.再统一释放剩下的空间;

        

        把内存分成两份,一次只用其中的一半,这个方案可以有效解决内存碎片的问题.但是缺点也很明显:
1、内存要浪费一半,利用率不高;
2、如果有效的对象非常多,拷贝开销就很大;

3.2.3 标记整理

        既能够解决内存碎片的问题,又能处理复制算法中利用率;

        类似于顺序表删除元素的搬运操作

3.2.4 分代回收

        实际上,JVM 采取的释放思路是上述基础思路结合体,分代回收,对象能活过的 GC 扫描轮次越多, 就是越老;

        伊甸区:

        刚 new 的新的对象放到伊甸区,从对象诞生,到第一轮可达性分析扫描,这个过程中
虽然时间不长(往往就是毫秒或秒)但是,在这个时间里,大部分的对象都会成为垃圾,

        释放过程如下所示:

1)、伊甸区 =>幸存区

         使用复制算法,每一轮 GC 扫描之后, 都把有效对象复制到幸存区中,伊甸区就可以整个释放了,由于经验规律,真正需要复制的对象不多,所以非常适合复制算法;

2)、GC 扫描线程也会扫描幸存区.
        就会把活过GC 扫描的对象(扫描过程中可达),拷贝到幸存区的另一个部分,幸存区之间的拷贝,每一轮会拷贝多个对象,每一轮也会淘汰掉一批对象(有些对象随着时间的推移,就成了垃圾)

3)、当这个对象已经在幸存区存活过很多轮 GC 扫描之后,JVM 就认为这个对象,短时间内应该是释放不掉了,就会把这个对象拷贝到老年代;

4)、进入老年代的对象, 虽然也会被 GC 扫描,但是老年代 GC 扫描的频率就会比新生代;

        分代回收,是 JVM 中主要的回收的思想方法.,但是在垃圾回收器具体实现的时候,可能还会有一些调整和优化.

ps:到这里java ee初阶的内容就结束了,感谢陪伴了很久的自己,尤其是在上完班后晚上敲写博客的自己,还是那句话,虽然多阻滞,花发再重荣!!!

        

相关文章:

【Java EE初阶三十】JVM的简单学习

1. JVM 内存区域划分 一个运行起来的 Java 进程,就是一个 JVM 虚拟机,需要从操作系统申请一大块内存,就会把这个内存,划分成不同的区域,每个区域都有不同的作用. JVM 申请了一大块内存之后,也会划分成不同的内…...

thinkphp5水平分割表partition,以及查询操作

前言 先交代下背景,在一个项目中,有一个数据表有水平分表的需求。当时想找到一种方法,把对数据库的操作,写到一个模型里,通过去换模型属性中的table来达到代码不变操作的数据表变化的效果。 我们都知道,模型要想关联数据表的话&a…...

docker部署aria2-pro

前言 我平时有一些下载视频和一些资源文件的需求,有时候需要离线下载,也要速度比较快的方式 之前我是用家里的玩客云绝育之后不再写盘当下载机用的,但是限制很多 我发现了aria2 这个下载器非常适合我,而有个大佬又在原来的基础…...

vue中Mixins

使用 Mixins 的主要优点包括: 代码复用: 可以将常用的逻辑封装在 Mixin 中,然后在多个组件中重复使用。逻辑分离: 将不同功能的代码分开管理,使代码更加清晰和易于维护。灵活性: Mixins 允许你在组件中引入多个 Mixin,并且可以根…...

linux常用指令(定期更新)

linux常用指令 1.页相关页大小 2.系统参数3.启动参数4.网络参数查询网卡所属numa节点信息网络测速相关iperf测试sar监控网卡流量查看网卡txqueuelen和mtu抓包tcpdump 网络数据收发状态snmp协议栈netstat -i所有网口TX-OK、RX-OKnetstat-s查看各个协议的收发数据ethtool -S单个网…...

【项目】图书管理系统

目录 前言: 项目要求: 知识储备: 代码实现: Main: Books包: Book: BookList: Operate包: Operate: addOperate: deleteOperate: exitOperate: findOperate:…...

华为OD机试 - 疫情扩散时间计算 - 矩阵(Java 2024 C卷 200分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2024C卷题库疯狂收录中,刷题点这里 专栏导读 本专栏收录于《华为OD机试(JAVA)真题(A卷B卷C卷&am…...

[数据集][图像分类]棉花叶子病害分类数据集2293张4类别

数据集类型:图像分类用,不可用于目标检测无标注文件 数据集格式:仅仅包含jpg图片,每个类别文件夹下面存放着对应图片 图片数量(jpg文件个数):2293 分类类别数:4 类别名称:["diseased_cotton_leaf"…...

《辐射4》是一款什么样的游戏 怎样在mac电脑上玩到《辐射4》辐射4攻略 辐射4开局加点 怎么在Mac电脑玩Steam游戏

辐射4(Fallout 4)是由Bethesda开发的一款动作角色扮演类游戏,为《辐射》系列游戏作品的第四代,于2015年11月10日发行。游戏叙述了主角一家在核爆当天(2077年10月23日),被Vault-Tec(避…...

视频推拉流EasyDSS平台直播通道重连无法转推的原因排查与解决

视频推拉流EasyDSS视频直播点播平台,集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体,可提供音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务。 用户使用EasyDSS平台对直播通道进行转推,发现只要关闭…...

Javaweb之SpringBootWeb案例之自动配置案例的自定义starter测试的详细解析

3.2.4.3 自定义starter测试 阿里云OSS的starter我们刚才已经定义好了,接下来我们就来做一个测试。 今天的课程资料当中,提供了一个自定义starter的测试工程。我们直接打开文件夹,里面有一个测试工程。测试工程就是springboot-autoconfigurat…...

java包的相关概念

包:有效地管理类的一个机制。 包名的目的:通过隶属不同的包有效的区分不同源文件中名字相同的类 包语句 声明:通过关键字 package声明包语句。 位置:源文件中的第一条语句。 作用:为该源文件中声明的类指定包名。 …...

window搭建本地mongo数据库并导入数据

1 window下载mongo数据库 官网:www.mongodb.com 选择 Products > Community Edition 就能进入社区版 在这里下载 windows 版对应的安装包 注意:6.0.1 版本的 MongoDB 配置环境变量有问题,并且我不知道怎么解决,如果想要避免出…...

如何为Android车载应用开发通知?

如何为Android车载应用开发通知?在开发车载应用的通知时,开发者需要考虑到驾驶安全,确保通知不会分散驾驶员的注意力。这通常意味着通知应该是非侵入性的,或者在不影响驾驶的情况下提供信息。开发者可以使用Android的通知API来创建…...

centos上部署k8s

环境准备 四台Linux服务器 主机名 IP 角色 k8s-master-94 192.168.0.94 master k8s-node1-95 192.168.0.95 node1 k8s-node2-96 192.168.0.96 node2 habor 192.168.0.77 镜像仓库 三台机器均执行以下命令: 查看centos版本 [rootlocalhost Work]# cat /…...

网络安全: Kali Linux 进行 MSFvenom 程序利用

目录 一、实验 1.环境 2. Kali Linux 进行 MSFvenom 程序利用 3. 创建计划任务自动运行 MSFvenom 程序 二、问题 1.在线加密解密 2.MSF 运行失败 3.MobaXterm 连接Ubuntu 失败 一、实验 1.环境 (1)主机 表1 主机 系统版本IP备注Kali Linux20…...

浅显易懂C语言指针!!!(三)

文章目录 Pointers as function arguments - call by reference//函数传值vs传引用 Pointers as function arguments - call by reference//函数传值vs传引用 #include<stdio.h> void Increment(int a){//increment 增加 a a 1;//&#xff1b;函数中的是形式参数 形参…...

01 LM 算法及 Cpp 实现

文章目录 01 LM 算法及 Cpp 实现1.1 应用1.2 阻尼法推导1.3 Cpp 算法实现 01 LM 算法及 Cpp 实现 1.1 应用 LM 算法用于解决非线性最小二乘问题 min ⁡ x F ( x ) 1 2 ∥ f ( x ) ∥ 2 2 (1) \min _x F(x)\frac{1}{2}\|f(\boldsymbol{x})\|_2^2 \tag{1} xmin​F(x)21​∥f(x…...

【网络安全架构】互联网正对中国社会、经济、文化等各个领域产生巨大影响‘

摘 要&#xff1a; 中国互联网近年来飞速发展&#xff0c;普及率达到38.4%。已稳居世界第一网民大国的地位。互联网正对社会、经济、文化等各个领域产生巨大影响。2011年12月21日“泄密门”事件&#xff0c;再次敲响网络安全的警钟。网络攻击和入侵都是根据网络模型不同层次的特…...

【笔记】Android ServiceStateTracker 网络状态变化逻辑及SPN更新影响

业务简介 在网络状态变化的时候&#xff08;数据或WiFi&#xff09;&#xff0c;会更新SPN。 基于Android U的代码分析。 分类&#xff1a;SPN Data_Dic-的博客-CSDN博客 功能逻辑 状态说明 飞行模式下注册上WFC的话&#xff0c;注册状态MD上报 regState: NOT_REG_MT_NOT…...

Java 语言特性(面试系列2)

一、SQL 基础 1. 复杂查询 &#xff08;1&#xff09;连接查询&#xff08;JOIN&#xff09; 内连接&#xff08;INNER JOIN&#xff09;&#xff1a;返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

MFC内存泄露

1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...

macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用

文章目录 问题现象问题原因解决办法 问题现象 macOS启动台&#xff08;Launchpad&#xff09;多出来了&#xff1a;Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显&#xff0c;都是Google家的办公全家桶。这些应用并不是通过独立安装的…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

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

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