JVM学习-内存结构(二)

article/2025/7/16 18:21:20

一、堆

1.定义

2.堆内存溢出问题

1.演示

-Xmx设置堆大小

3.堆内存的诊断

3.1介绍

1,2都是命令行工具(可直接在ideal运行时,在底下打开终端,输入命令)

1可以拿到Java进程的进程ID,2 jmap只能查询某一个时刻的堆内存

3.2 堆内存jmap演示

public class Demo1_4 {
​public static void main(String[] args) throws InterruptedException {System.out.println("1...");Thread.sleep(30000);byte[] array = new byte[1024 * 1024 * 10]; // 10 MbSystem.out.println("2...");Thread.sleep(20000);array = null;System.gc();System.out.println("3...");Thread.sleep(1000000L);}
}

第一个:

第二个(Eden多了10M):

第三个:垃圾回收后

3.3jconsole演示

运行程序 在终端输入jconsole,弹出界面

选择对应的程序,点击不安全连接

3.4.案例二

现象垃圾回收后,内存仍然占用很高

工具:jvisualvm

代码

/*** 演示查看对象个数 堆转储 dump*/
public class Demo1_13 {
​public static void main(String[] args) throws InterruptedException {List<Student> students = new ArrayList<>();for (int i = 0; i < 200; i++) {students.add(new Student());
//            Student student = new Student();}Thread.sleep(1000000000L);}
}
class Student {private byte[] big = new byte[1024*1024];
}

点击堆Dump,进行查找

二、方法区

1.定义

        Java 虚拟机有一个在所有 Java 虚拟机线程之间共享的方法区域。方法区域类似于用于传统语言的编译代码的存储区域,或者类似于操作系统进程中的“文本”段。它存储每个类的结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括特殊方法,用于类和实例初始化以及接口初始化方法区域是在虚拟机启动时创建的。尽管方法区域在逻辑上是堆的一部分,但简单的实现可能不会选择垃圾收集或压缩它。此规范不强制指定方法区的位置或用于管理已编译代码的策略。方法区域可以具有固定的大小,或者可以根据计算的需要进行扩展,并且如果不需要更大的方法区域,则可以收缩。方法区域的内存不需要是连续的!

        存的是跟类相关的信息,包括方法,构造器,成员方法等。方法区在虚拟机启动时被创建是在概念上定义的方法区,逻辑上属于堆的组成部分(厂家实现不同)方法区也会导致内存溢出的错误,抛出OutOfMemoryEror

2.组成

以Hotspot 虚拟机为例,jdk1.6 1.7 1.8 内存结构图,1.8使用的是本地内存不在占用堆内存 。

3.方法区内存溢出

  • 1.8 之前会导致永久代内存溢出

    使用 -XX:MaxPermSize=8m 指定永久代内存大小
  • 1.8 之后会导致元空间内存溢出

    • 使用 -XX:MaxMetaspaceSize=8m 指定元空间大小

  • 设置元空间大小为8m

    public class Demo1_8 extends ClassLoader { // 类加载器 可以用来加载类的二进制字节码public static void main(String[] args) {int j = 0;try {Demo1_8 test = new Demo1_8();for (int i = 0; i < 10000; i++, j++) {// ClassWriter 作用是生成类的二进制字节码ClassWriter cw = new ClassWriter(0);// 版本号, public, 类名, 包名, 父类, 接口cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, "Class" + i, null, "java/lang/Object", null);// 返回 byte[]byte[] code = cw.toByteArray();// 执行了类的加载test.defineClass("Class" + i, code, 0, code.length); // Class 对象}} finally {System.out.println(j);}}
    }

4.运行时常量池

4.1.二进制字节码文件的构成

主要分为(类的基本信息,常量池,类方法定义,包含的虚拟机指令)

可将程序运行产生的.class文件通过 javap 命令反编译

源程序

package jvm;
​
​
public class HelloWorld {public static void main(String[] args) {System.out.println("hello world");}
}

切换到out输出目录下,使用javap命令

得到反编译后的

 Last modified 2024-12-27; size 541 bytesMD5 checksum 1705415cdaac31d861d20edc1e472d95Compiled from "HelloWorld.java"
public class jvm.HelloWorldminor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER
Constant pool:#1 = Methodref          #6.#20         // java/lang/Object."<init>":()V#2 = Fieldref           #21.#22        // java/lang/System.out:Ljava/io/PrintStream;#3 = String             #23            // hello world#4 = Methodref          #24.#25        // java/io/PrintStream.println:(Ljava/lang/String;)V#5 = Class              #26            // jvm/HelloWorld#6 = Class              #27            // java/lang/Object#7 = Utf8               <init>#8 = Utf8               ()V#9 = Utf8               Code#10 = Utf8               LineNumberTable#11 = Utf8               LocalVariableTable#12 = Utf8               this#13 = Utf8               Ljvm/HelloWorld;#14 = Utf8               main#15 = Utf8               ([Ljava/lang/String;)V#16 = Utf8               args#17 = Utf8               [Ljava/lang/String;#18 = Utf8               SourceFile#19 = Utf8               HelloWorld.java#20 = NameAndType        #7:#8          // "<init>":()V#21 = Class              #28            // java/lang/System#22 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;#23 = Utf8               hello world#24 = Class              #31            // java/io/PrintStream#25 = NameAndType        #32:#33        // println:(Ljava/lang/String;)V#26 = Utf8               jvm/HelloWorld#27 = Utf8               java/lang/Object#28 = Utf8               java/lang/System#29 = Utf8               out#30 = Utf8               Ljava/io/PrintStream;#31 = Utf8               java/io/PrintStream#32 = Utf8               println#33 = Utf8               (Ljava/lang/String;)V
{public jvm.HelloWorld();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 4: 0LocalVariableTable:Start  Length  Slot  Name   Signature0       5     0  this   Ljvm/HelloWorld;public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=1, args_size=10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #3                  // String hello world5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnLineNumberTable:line 6: 0line 7: 8LocalVariableTable:Start  Length  Slot  Name   Signature0       9     0  args   [Ljava/lang/String;
}
SourceFile: "HelloWorld.java"

在代码区域,每条指令都会对应常量池表中一个地址,常量池表中的地址可能对应着一个类名、方法名、参数类型等信息。

ldc #3 在常量池中找一个编号为3的符号

4.2定义

常量池: 就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量信息 运行时常量池: 常量池是 *.class 文件中的,当该类被加载以后,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

4.3 StringTable

StringTable底层是一个哈希表

下面这些题你能作对吗:

答案:
public class Demo1 {
​public static void main(String[] args) {String s1 = "a";String s2 = "b";String s3 = "a" + "b"; // ab 常量池中String s4 = s1 + s2;   // new String("ab") 堆中String s5 = "ab";String s6 = s4.intern();
​
// 问System.out.println(s3 == s4); // falseSystem.out.println(s3 == s5); // trueSystem.out.println(s3 == s6); // true
​String x2 = new String("c") + new String("d"); // new String("cd") 堆中x2.intern();String x1 = "cd";//现在是true     上放一行 x1 != x2(false)
​
// 如果是jdk1.6呢(1.6是将x2拷贝一份入池,1.8是本身入池)System.out.println(x1 == x2);//现在是false     上放一行 x1 != x2(false)}
}
4.3.1StringTable的特性
  1. 常量池中的字符串仅是符号,只有在被用到时才会转化为对象

  2. 利用串池的机制,来避免重复创建字符串对象

  3. 字符串变量拼接的原理是StringBuilder(1.8)

  4. 字符串常量拼接的原理是编译器优化

  5. 可以使用intern方法,主动将串池中还没有的字符串对象放入串池中

    package jvm;
    ​
    public class Demo {
    ​//  ["ab", "a", "b"]public static void main(String[] args) {
    ​String x = "ab";String s = new String("a") + new String("b");
    ​// 堆  new String("a")   new String("b") new String("ab")这个ab是动态拼接的不在串池中,在堆中String s2 = s.intern(); // 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回
    ​System.out.println( s2 == "ab");//trueSystem.out.println( s == "ab");//trueSystem.out.println( s2 == x);//trueSystem.out.println( s == x );//false}
    ​
    }

intern方法 1.8 调用字符串对象的 intern 方法,会将该字符串对象尝试放入到串池中

  1. 如果串池中没有该字符串对象,则放入成功

  2. 如果有该字符串对象,则放入失败 无论放入是否成功,都会返回串池中的字符串对象

  3. 注意:此时如果调用 intern 方法成功,堆内存与串池中的字符串对象是同一个对象;如果失败,则不是同一个对象

4.4 StringTable的位置

jdk1.6 StringTable 位置是在永久代中,1.7,1.8 StringTable 位置是在堆中。

4.5 串池 、常量池 、 运行时常量池 的关系:

  1. 常量池(静态常量池):

    • 常量池通常指的是.class文件中的常量池,它包含了类、方法、字段的符号引用,以及字面量等信息。这些信息在类加载到JVM之前就已经确定,并且存储在.class文件中。

  2. 运行时常量池

    • 当类被加载到JVM时,其常量池的信息会被复制到运行时常量池中。运行时常量池是方法区的一部分,它包含了从.class文件中复制来的常量,以及在运行时动态生成的常量。

    • 运行时常量池相对于.class文件中的常量池具有动态性,可以在运行时添加新的常量。

  3. StringTable(串池)

    • StringTable是运行时常量池的一部分,专门用于存储字符串常量。它通过一个哈希表(数组+链表)来实现,确保存储的字符串常量唯一且不重复。

    • 在JDK 1.7及之前版本中,StringTable位于方法区(Perm Gen),而在JDK 1.8及之后版本中,StringTable被移到了堆中。

    • StringTable中存储的并不是String对象本身,而是指向堆中String对象的引用。

    • StringTable的创建是懒加载的,即只有当字符串常量第一次被使用时,才会在堆中创建String对象,并将其引用放入StringTable中。

        总结来说,StringTable是运行时常量池中专门用于管理字符串常量的部分,它通过优化存储机制来确保相同内容的字符串对象在JVM中只存在一份,从而节省内存。而常量池和运行时常量池则包含了更广泛的信息,包括类、方法、字段的引用和字面量等。StringTable与常量池的关系在于,常量池中的字符串常量在类加载时会被复制到运行时常量池中的StringTable里,而运行时常量池则包含了常量池的所有内容,并支持动态添加新的常

4.6 StringTable 垃圾回收

StringTable底层是一个哈希表

先设置虚拟机参数(便于输出观察):

-Xmx10m 指定堆内存大小 -XX:+PrintStringTableStatistics 打印字符串常量池信息 -XX:+PrintGCDetails -verbose:gc 打印 gc 的次数,耗费时间等信息

public class StringTable {public static void main(String[] args) throws InterruptedException {int i = 0;try {for (int j = 0; j < 100000; j++) { // j=100, j=10000String.valueOf(j).intern();i++;}} catch (Throwable e) {e.printStackTrace();} finally {System.out.println(i);}
​}
}

4.7StringTable的性能调优

因为StringTable是由HashTable实现的,所以可以适当增加HashTable桶的个数,来减少字符串放入串池所需要的时间

使用时加入配置:-Xms500m -Xmx500m -XX:+PrintStringTableStatistics -XX:StringTableSize=1009 (字符串很多时考虑)

-XX:StringTableSize=桶个数(最少设置为 1009 以上)

考虑是否需要将字符串对象入池 可以通过 intern 方法减少重复入池

三、直接内存

1.定义

操作系统的内存 Direct Memory

  • 常见于 NIO 操作时,用于数据缓冲区

  • 分配回收成本较高,但读写性能高

  • 不受 JVM 内存回收管理

2.使用直接内存的好处

2.1文件读写流程:

        因为 java 不能直接操作文件管理,需要切换到内核态,使用本地方法进行操作,然后读取磁盘文件,会在系统内存中创建一个缓冲区,将数据读到系统缓冲区, 然后在将系统缓冲区数据,复制到 java 堆内存中。缺点是数据存储了两份,在系统内存中有一份,java 堆中有一份,造成了不必要的复制。

2.2使用了 DirectBuffer 文件读取流程

直接内存是操作系统和 Java 代码都可以访问的一块区域,无需将代码从系统内存复制到 Java 堆内存,从而提高了效率。

3.直接内存回收原理

public class Code_06_DirectMemoryTest {public static int _1GB = 1024 * 1024 * 1024;public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
//        method();method1();}// 演示 直接内存 是被 unsafe 创建与回收private static void method1() throws IOException, NoSuchFieldException, IllegalAccessException {Field field = Unsafe.class.getDeclaredField("theUnsafe");field.setAccessible(true);Unsafe unsafe = (Unsafe)field.get(Unsafe.class);long base = unsafe.allocateMemory(_1GB);unsafe.setMemory(base,_1GB, (byte)0);System.in.read();unsafe.freeMemory(base);System.in.read();}// 演示 直接内存被 释放private static void method() throws IOException {ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1GB);System.out.println("分配完毕");System.in.read();System.out.println("开始释放");byteBuffer = null;System.gc(); // 手动 gcSystem.in.read();}}

        直接内存的回收不是通过 JVM 的垃圾回收来释放的,而是通过unsafe.freeMemory 来手动释放。 第一步:allocateDirect 的实现

public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);
}

 底层是创建了一个 DirectByteBuffer 对象。

第二步:DirectByteBuffer 类

DirectByteBuffer(int cap) {   // package-privatesuper(-1, 0, cap, cap);boolean pa = VM.isDirectMemoryPageAligned();int ps = Bits.pageSize();long size = Math.max(1L, (long)cap + (pa ? ps : 0));Bits.reserveMemory(size, cap);long base = 0;try {base = unsafe.allocateMemory(size); // 申请内存} catch (OutOfMemoryError x) {Bits.unreserveMemory(size, cap);throw x;}unsafe.setMemory(base, size, (byte) 0);if (pa && (base % ps != 0)) {// Round up to page boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); // 通过虚引用,来实现直接内存的释放,this为虚引用的实际对象, 第二个参数是一个回调,实现了 runnable 接口,run 方法中通过 unsafe 释放内存。att = null;
}

        这里调用了一个 Cleaner 的 create 方法,且后台线程还会对虚引用的对象监测,如果虚引用的实际对象(这里是 DirectByteBuffer )被回收以后,就会调用 Cleaner 的 clean 方法,来清除直接内存中占用的内存。

 public void clean() {if (remove(this)) {try {// 都用函数的 run 方法, 释放内存this.thunk.run();} catch (final Throwable var2) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {if (System.err != null) {(new Error("Cleaner terminated abnormally", var2)).printStackTrace();}System.exit(1);return null;}});}}}

可以看到关键的一行代码, this.thunk.run(),thunk 是 Runnable 对象。run 方法就是回调 Deallocator 中的 run 方法,

		public void run() {if (address == 0) {// Paranoiareturn;}// 释放内存unsafe.freeMemory(address);address = 0;Bits.unreserveMemory(size, capacity);}

直接内存的回收机制总结

   使用了 Unsafe 类来完成直接内存的分配回收,回收需要主动调用freeMemory 方法

   ByteBuffer 的实现内部使用了 Cleaner(虚引用)来检测 ByteBuffer 。一旦ByteBuffer 被垃圾回收,那么会由 ReferenceHandler(守护线程) 来调用 Cleaner 的 clean 方法调用 freeMemory 来释放内存

注意:

/*** -XX:+DisableExplicitGC 显示的*/private static void method() throws IOException {ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1GB);System.out.println("分配完毕");System.in.read();System.out.println("开始释放");byteBuffer = null;System.gc(); // 手动 gc 失效System.in.read();}

一般用 jvm 调优时,会加上下面的参数:

-XX:+DisableExplicitGC  // 静止显示的 GC

        意思就是禁止我们手动的 GC,比如手动 System.gc() 无效,它是一种 full gc,会回收新生代、老年代,会造成程序执行的时间比较长。所以我们就通过 unsafe 对象调用 freeMemory 的方式释放内存。


http://www.hkcw.cn/article/mohSGvxpct.shtml

相关文章

JVM相关内容

jvm的跨平台&#xff0c;字节码的作用 jvm的跨平台 不同操作系统系统运行的JVM不一样&#xff0c;但度能够处理对应的字节码文件 字节码的作用 利用编译节省了运行的时候的效率 JVM整体结构 类加载子系统&#xff1a;用于加载不同的class&#xff08;字节码&#xff09;文…

Sqlite3数据库表内数据批量读取操作---sqlite3_stmt机制

0、引言 在前面两篇文章已经对数据环境搭建、数据批量写入库中进行了较为详细的讲解。因此&#xff0c;基于前两篇文章内容的基础上&#xff0c;本文主要从数据库中批量数据读取操作进行梳理讲解。 嵌入式数据库SQLite 3配置使用详细笔记教程_sqlite3-CSDN博客 SQLite 3 优化批…

官方指定Jmeter配置JVM堆内存方式

软件测试资料领取&#xff1a;[内部资源] 想拿年薪40W的软件测试人员&#xff0c;这份资料必须领取~ 软件测试面试刷题工具领取&#xff1a;软件测试面试刷题【800道面试题答案免费刷】 1.概述 在使用Jmeter做性能测试过程中&#xff0c;可能会应为默认设置的堆内存值较小出…

线上JVM OOM问题,如何排查和解决?

今天咱们来聊聊让无数 Java 开发者头疼的 JVM OOM&#xff08;Out Of Memory&#xff0c;内存溢出&#xff09;问题。在面试中&#xff0c;OOM 问题也是面试官的“心头好”&#xff0c;因为它能直接考察你对 JVM 的理解&#xff0c;以及你在实际问题面前的排查和解决能力。 一…

JVM常见线上问题:CPU 100%、内存泄露问题排查

一、CPU 100% 问题排查 1.1、找到 cpu 占有率最高的 java 进程号 使用命令: top -c 显示运行中的进程列表信息, shift + p 使列表按 cpu 使用率排序显示。 PID = 2227 的进程,cpu 使用率最高 1.2、根据进程号找到 cpu 占有率最高的线程号 使用命令: top -Hp {pid} ,同…

JVM 一文详解

目录 JVM 简介 JVM 中的内存区域划分 1. 堆&#xff08;一个进程只有一份 ------ 线程共享&#xff09; 2. 栈&#xff08;一个进程可以有 N 份 ------ 线程私有&#xff09; Java 虚拟机栈&#xff1a; 本机方法栈&#xff1a; 3. 程序计数器&#xff08;一个线程可以…

【JVM】关于JVM的内部原理你到底了解多少(八股文面经知识点)

前言 &#x1f31f;&#x1f31f;本期讲解关于HTTPS的重要的加密原理~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废话不…

深入理解 JVM 的栈帧结构

🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程,高并发设计,Springboot和微服务,熟悉Linux,ESXI虚拟化以及云原生Docker和K8s…

JVM 机制

目录 一、什么是 JVM&#xff1a; 二、JVM 的运行流程&#xff1a; 三、JVM 内存区域划分&#xff1a; 1、( 1 ) 程序计数器&#xff1a; 1、( 2 ) 元数据区&#xff1a; 1、( 3 ) 栈&#xff1a; 1、( 4 ) 堆&#xff1a; 四、类加载&#xff1a; 1、什么时候会触…

【JVM】类加载机制

文章目录 类加载机制类加载过程1. 加载2. 验证3. 准备4. 解析偏移量符号引用和直接引用 5. 初始化 类加载机制 类加载指的是&#xff0c;Java 进程运行的时候&#xff0c;需要把 .class 文件从硬盘读取到内存&#xff0c;并进行一些列的校验解析的过程&#xff08;程序要想执行…

【JVM】从零开始深度解析JVM

本篇博客给大家带来的是JVM的知识点, 重点在类加载和垃圾回收机制上. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; …

一篇文章带你解决笔试面试中的jvm问题

JVM内存区域划分 JVM启动的时候,会申请到一整个很大的内存区域.JVM是一个应用程序,要从操作系统里申请内存.JVM就根据需要,把空间分为几个部分,每个部分各自有不同的功能.具体划分如下: 分为&#xff1a;栈&#xff0c;堆&#xff0c;程序计数器&#xff0c;元数据区 Heap(堆):…

通义灵码2.5——基于编程智能体开发Wiki多功能搜索引擎

引言 在智能化浪潮重塑软件开发范式的今天&#xff0c;我借助开发一个基于编程智能体开发Wiki 多功能搜索引擎&#xff0c;深度体验了通义灵码2.5这一阿里云旗舰级AI编码助手&#xff0c;构建智能协作新范式。 该平台通过三大技术突破赋能开发全流程&#xff1a;基于编程智能…

基于SpringBoot的商家销售管理网站的设计与实现

湖南软件职业技术大学 本科毕业设计(论文) 设计(论文)题目 基于SpringBoot的商家销售管理网站的设计与实现 学生姓名 学生学号 所在学院 专业班级 校内指导教师 企业指导教师 毕业设计(论文)真实性承诺及声明 学生对毕业设计(论文)真实性承诺 本人郑重声明:所提交的毕…

各种噪声电流激励下电源PDN网络对系统时钟性能的影响

点击上面“蓝字”关注我们 电源分配是支持所有类型硅产品运行的基础设施的重要组成部分&#xff0c;但在设计过程中常被忽视。电源质量可能限制电路性能&#xff0c;并决定其工作可靠性。要真正解决电源分配问题&#xff0c;必须考虑包括芯片、封装和PCB在内的整个系统。 芯片…

【大模型】情绪对话模型项目研发

一、使用框架&#xff1a; Qwen大模型后端Open-webui前端实现使用LLamaFactory的STF微调数据集&#xff0c;vllm后端部署&#xff0c; 二、框架安装 下载千问大模型 安装魔塔社区库文件 pip install modelscope Download.py 内容 from modelscope import snapshot_downlo…

关于位图Bitmaps的介绍

目录 1、基本概念 1.1、介绍 1.2、关键字 1.3、结构原理 2、常用命令 2.1、SETBIT 2.2、GETBIT 2.3、BITCOUNT 2.4、BITOP 2.5、BITPOS 3、应用场景 4、使用示例 前言 Redis的Bitmaps是一种基于字符串的数据结构&#xff0c;用于处理位级别的操作。 Bitmaps在Redis…

【软件设计】通过软件设计提高 Flash 的擦写次数

目录 0. 个人简介 && 授权须知1. Flash 和 EEROM 基本情况2. 场景要求3. 软件设计思路4. 代码展示4.1 flash.h4.2 flash.c 0. 个人简介 && 授权须知 &#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是喜欢记录零碎知识点的菜鸟…

C 语言练习--初级

#学习C 代码&#xff0c; 做小练习时&#xff0c;自己运行代码竟然发现很多错误&#xff0c;记录一下。 1、计算器 根据输入的数值和符合&#xff0c;输出相应结果。 结果&#xff1a; #include "stdio.h"int Primary_math(int a, int b, char sign){int num0;swit…

pikachu通关教程-CSRF XSS

XSS XSS漏洞原理 XSS被称为跨站脚本攻击&#xff08;Cross Site Scripting&#xff09;&#xff0c;由于和层叠样式表&#xff08;Cascading Style Sheets&#xff0c;CSS&#xff09;重名&#xff0c;改为XSS。主要基于JavaScript语言进行恶意攻击&#xff0c;因为js非常灵活…