【Java八股文面试系列】JVM-内存区域

目录

Java内存区域

运行时数据区域

线程独享区域

程序计数器

Java 虚拟机栈

StackFlowError&OOM

本地方法栈

线程共享区域

GCR-分代回收算法

字符串常量池

方法区

运行时常量池

HotSpot 虚拟机对象探秘

对象的创建

对象的内存布局

句柄


Java内存区域

运行时数据区域

Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。

JDK 1.8 和之前的版本略有不同,我们这里以 JDK 1.7 和 JDK 1.8 这两个版本为例介绍。

JDK1.7运行时数据区域:

image-20240130135118481

JDK1.8运行时数据区域:

image-20240130135235723

Java的运行时区域可以分成两个大的部分,一个是所有线程共享的区域,一个是当前线程独享的区域。

线程独享的区域:

生命周期与线程相同,随着线程创建而创建,随着线程死亡而死亡

  • 程序计数器(相当于一个程序运行的光标,标记了程序的运行位置)

  • 虚拟机栈(使用了栈的存储结构,将方法的调用顺序进行记录,同时记录了方法的一些信息)

  • 本地方法栈

线程共享区域:

  • 堆(保存数组和对象的位置,但是数组其实也是对象)

  • 方法区

  • 直接内存(除了JVM定义的内存之外,运行的主机的运行内存上申请的内存)

Java 虚拟机规范对于运行时数据区域的规定是相当宽松的。以堆为例:堆可以是连续空间,也可以不连续。堆的大小可以固定,也可以在运行时按需扩展 。虚拟机实现者可以使用任何垃圾回收算法管理堆,甚至完全不进行垃圾收集也是可以的

线程独享区域

程序计数器

是什么?

是当前线程所执行的字节码的行号指示器。字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等功能都需要依赖这个计数器来完成。

有什么用?

字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。

在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。

注意:程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。

Java 虚拟机栈

是什么?

栈除了一些 Native 方法调用是通过本地方法栈实现的(后面会提到),其他所有的 Java 方法调用都是通过栈来实现的(也需要和其他运行时数据区域比如程序计数器配合)。

Native方法指的是指使用本地代码实现的方法。这些方法是在Java代码中声明的,但实际的实现代码是由非Java语言(如C、C++或其他语言)编写的,并且通常在Java虚拟机(JVM)外部运行。

使用native方法可以提供更高的性能和更低级别的控制,因为本地代码可以直接与操作系统和硬件交互。然而,这也增加了代码的复杂性和维护难度,因为本地代码需要单独编译和测试。

方法调用的数据需要通过栈进行传递,每一次方法调用都会有一个对应的栈帧被压入栈中,每一个方法调用结束后,都会有一个栈帧被弹出。

栈由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法返回地址。和数据结构上的栈类似,两者都是先进后出的数据结构,只支持出栈和入栈两种操作。

image-20240130141256561

局部变量表:主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。

操作数栈:主要作为方法调用的中转站使用,用于存放方法执行过程中产生的中间计算结果。另外,计算过程中产生的临时变量也会放在操作数栈中。

动态链接主要服务一个方法需要调用其他方法的场景。Class 文件的常量池里保存有大量的符号引用比如方法引用的符号引用。当一个方法要调用其他方法,需要将常量池中指向方法的符号引用转化为其在内存地址中的直接引用。动态链接的作用就是为了将符号引用转换为调用方法的直接引用,这个过程也被称为 动态连接

public class Main {
    public static void main(String[] args) {
        int i = 10;
        int a = i++;
        int b = ++i;
    }
    public void a() {
        System.out.println("aa");
        b();
    }
    public void b() {
        System.out.println("bbb");
    }
}

通过反编译可以看到方法在常量池都会有一个符号引用

image-20240130142048572

方法A中调用了B方法,所以方法A中会有方法B的应用,并且这里的引用#5都是和前面的常量池能够对上的

image-20240130142248290

StackFlowError&OOM

栈空间虽然不是无限的,但一般正常调用的情况下是不会出现问题的。不过,如果函数调用陷入无限循环的话,就会导致栈中被压入太多栈帧而占用太多空间,导致栈空间过深。那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。

Java 方法有两种返回方式,一种是 return 语句正常返回,一种是抛出异常。不管哪种返回方式,都会导致栈帧被弹出。也就是说, 栈帧随着方法调用而创建,随着方法结束而销毁。无论方法正常完成还是异常完成都算作方法结束。

除了 StackOverFlowError 错误之外,栈还可能会出现OutOfMemoryError错误,这是因为如果栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

简单总结一下程序运行中栈可能会出现两种错误:

  • StackOverFlowError 若栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前 Java 虚拟机栈的最大深度的时候,就抛出 StackOverFlowError 错误。

  • OutOfMemoryError 如果栈的内存大小可以动态扩展, 如果虚拟机在动态扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

img

本地方法栈

和虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。

方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowErrorOutOfMemoryError 两种错误。

线程共享区域

Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆(Garbage Collected Heap)。从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代;再细致一点有:Eden、Survivor、Old 等空间(空间的大小比为8:1:1)。进一步划分的目的是更好地回收内存,或者更快地分配内存。

image-20240130142954881

在 JDK 7 版本及 JDK 7 版本之前,堆内存被通常分为下面三部分:

  1. 新生代内存(Young Generation)

  2. 老生代(Old Generation)

  3. 永久代(Permanent Generation)

JDK 8 版本之后 PermGen(永久代) 已被 Metaspace(元空间) 取代,元空间使用的是本地内存。 (我会在方法区这部分内容详细介绍到)。

GCR-分代回收算法

上面的分代,也是涉及到了垃圾回收算法中的分代回收算法。

首先,所有新创建的对象,在一开始都会进入到新生代的Eden区(如果是大对象会被直接丢进老年代),在进行新生代区域的垃圾回收时,首先会对所有新生代区域的对象进行扫描,并回收那些不再使用对象:

image-20240129140902070

接着,在一次垃圾回收之后,Eden区域没有被回收的对象,会进入到Survivor区。在一开始From和To都是空的,而GC之后,所有Eden区域存活的对象都会直接被放入到From区,最后From和To会发生一次交换,也就是说目前存放我们对象的From区,变为To区,而To区变为From区:

image-20240129141220032

接着就是下一次垃圾回收了,操作与上面是一样的,不过这时由于我们To区域中已经存在对象了,所以,在Eden区的存活对象复制到From区之后,所有To区域中的对象会进行年龄判定(每经历一轮GC年龄+1,如果对象的年龄大于默认值为15,那么会直接进入到老年代,否则移动到From区)

image-20240129141234946

最后像上面一样交换To区和From区,之后不断重复以上步骤。

字符串常量池

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。字符串常量池从1.7开始就是从方法去移动到了堆中,并且静态变量都保存在里面

为什么移到堆中?

在JDK 1.7之前,字符串常量池存放在方法区中,字符串常量在编译时分配在常量池中。然而,这种方法存在一些限制,例如方法区的大小是固定的,如果方法区溢出,会导致OutOfMemoryError错误。此外,由于方法区的内存分配和回收相对较慢,只有FULL GC才会垃圾回收,因此可能会影响程序的性能。

HotSpot 虚拟机中字符串常量池的实现是 src/hotspot/share/classfile/stringTable.cpp ,StringTable 可以简单理解为一个固定大小的HashTable ,容量为 StringTableSize(可以通过 -XX:StringTableSize 参数来设置),保存的是字符串(key)和 字符串对象的引用(value)的映射关系,字符串对象的引用指向堆中的字符串对象。

方法区

当虚拟机要使用一个类时,它需要读取并解析 Class 文件获取相关信息,再将信息存入到方法区。方法区会存储已被虚拟机加载的 类信息、字段信息、方法信息、常量、静态变量、即时编译器编译后的代码缓存等数据

方法区和永久代以及元空间是什么关系呢?

方法区和永久代以及元空间的关系很像 Java 中接口和类的关系,类实现了接口,这里的类就可以看作是永久代和元空间,接口可以看作是方法区,也就是说永久代以及元空间是 HotSpot 虚拟机对虚拟机规范中方法区的两种实现方式。并且,永久代是 JDK 1.8 之前的方法区实现,JDK 1.8 及以后方法区的实现变成了元空间。

为什么要将永久代 (PermGen) 替换为元空间 (MetaSpace) 呢?

1、整个永久代有一个 JVM 本身设置的固定大小上限,无法进行调整,而元空间使用的是本地内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。

当元空间溢出时会得到如下错误:java.lang.OutOfMemoryError: MetaSpace

你可以使用 -XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。-XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。

2、元空间里面存放的是类的元数据,这样加载多少类的元数据就不由 MaxPermSize 控制了, 而由系统的实际可用空间来控制,这样能加载的类就更多了。

3、在 JDK8,合并 HotSpot 和 JRockit 的代码时, JRockit 从来没有一个叫永久代的东西, 合并之后就没有必要额外的设置这么一个永久代的地方了。

方法区常用参数有哪些?

JDK 1.8 之前永久代还没被彻底移除的时候通常通过下面这些参数来调节方法区大小。

-XX:PermSize=N //方法区 (永久代) 初始大小
-XX:MaxPermSize=N //方法区 (永久代) 最大大小,超过这个值将会抛出 OutOfMemoryError 异常:java.lang.OutOfMemoryError: PermGen

相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。

JDK 1.8 的时候,方法区(HotSpot 的永久代)被彻底移除了(JDK1.7 就已经开始了),取而代之是元空间,元空间使用的是本地内存。下面是一些常用参数:

-XX:MetaspaceSize=N //设置 Metaspace 的初始(和最小大小)
-XX:MaxMetaspaceSize=N //设置 Metaspace 的最大大小

与永久代很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存。

运行时常量池

运行时常量池的功能类似于传统编程语言的符号表,尽管它包含了比典型符号表更广泛的数据。

既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出 OutOfMemoryError 错误

HotSpot 虚拟机对象探秘

HotSpot虚拟机是Java虚拟机的一种实现,它是Sun Microsystems公司开发的一款高性能、动态类型的计算机编程语言虚拟机。HotSpot虚拟机采用了即时编译(JIT)技术,可以将Java字节码转换为本地机器代码,从而提高程序的执行效率。

对象的创建

Java 对象的创建过程——五步

  1. 检查类是否已经被加载

    new关键字时创建对象时,首先会去运行时常量池中查找该引用所指向的类有没有被虚拟机加载,如果没有被加载,那么会进行类的加载过程。(加载将class文件读取到内存中,使用加载器进行加载)

  2. 为对象分配内存空间

    当类加载检查通过后,虚拟机会为新生对象分配内存空间,对象所需内存空间的大小在类加载完成后就已经确定了。为新生对象分配内存空间其实就是在Java堆中划分出一块确定大小的内存分配给新生对象。分配内存的方式有“指针碰撞”和“空闲列表”两种,选择哪种分配方式取决于Java堆内存是否规整。

    内存分配的两种方式

    • 指针碰撞 使用场合:堆内存规整(即没有内存碎片)的情况下。 实现原理:将用过的内存都整合到一边,没有用过的内存放到另一边,中间有一个分界指针,当需要为新对象分配内存空间时,只需要将分界指针向没有用过的内存一侧移动对象内存大小位置即可。

    • 空闲列表 使用场合:堆内存不规整的情况下。(JDK8默认的GR垃圾回收方式,是不规整的方式) 实现原理:虚拟机会维护一个列表,该列表记录了哪些内存是可用的,当需要为新对象分配内存空间时,只需要在列表中找一块足够大小的内存分配给对象实例,然后更新列表记录。 选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器垃圾采用的垃圾收集算法

  3. 对象初始化零值

    内存分配完成后,虚拟机需要将新分配的内存空间都初始化为零值(不包括对象头),这一步操作保证了对象的实例字段可以在Java代码中可以不赋初始值就直接使用,程序能够访问这些实例字段的数据类型所对应的零值。

  4. 设置对象头

    初始化零值之后,虚拟机需要对对象头进行必要的设置,例如这个对象是哪个类的实例,如何才能找到这个类的元数据信息,对象的哈希码、GC 分代年龄、锁标志位、偏向锁标志位、线程持有的锁信息、对象是否可用等信息会存放到对象头中。另外,根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。

  5. 执行init方法

    执行完new指令后会接着执行init方法,将对象按照程序员的需求来进行初始化,这样一个真正可用的对象才算完全产生出来。

上述为无父类的对象创建过程。对于有父类的对象创建过程,还需满足如下条件:

  1. 先加载父类;再加载本类;

  2. 先执行父类的实例的初始化方法init(成员变量、构造代码块),父类的构造方法;执行本类的实例的初始化方法init(成员变量、构造代码块),本类的构造方法。

对象的内存布局

在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域:对象头实例数据对齐填充

image-20240122173034582

对象头:虚拟机的对象头包括两部分信息第一部分用于存储对象自身的运行时数据(哈希码、GC 分代年龄、锁状态标志等等),另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

实例数据:实例数据部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。

对齐填充:仅仅起占位作用,方便进行读取和运算

句柄

如果使用句柄的话,那么 Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与对象类型数据各自的具体地址信息。

对象的访问定位-使用句柄

对象的访问定位-使用句柄

如果使用直接指针访问,reference 中存储的直接就是对象的地址。

对象的访问定位-直接指针

对象的访问定位-直接指针

这两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。

参考文章

JavaGuide-JVM内存区域 

青空霞光-Jvm内存管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/371294.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如果通过浏览器调试?

背景:博主是一个有丰富经验的后端开发人员,在前端开发中感觉总是有种力不从心的感觉,因为没有后端debug调试的清晰感。 解决办法:掌握chorm浏览器调试技巧。 F12, F5 打上断点之后,这不就是梦寐之中的调试…

Jenkins升级后,构建任务配置界面重复错位

最近我把公司的Jenkins服务升级到了最新版本,升级完成后,点了一下构建任务,发现能够构建成功,就以为顺利完成升级了,下班走了,结果第二天,进入构建任务配置界面发现,界面一团乱麻&am…

LeetCode 133:克隆图(图的深度优先遍历DFS和广度优先遍历BFS)

回顾 图的Node数据结构 图的数据结构&#xff0c;以下两种都可以&#xff0c;dfs和bfs的板子是不变的。 class Node {public int val;public List<Node> neighbors;public Node() {val 0;neighbors new ArrayList<Node>();}public Node(int _val) {val _val;…

基于springboot地方美食分享网站源码和论文

基于springboot地方美食分享网站源码和论文361 首先&#xff0c;论文一开始便是清楚的论述了系统的研究内容。其次&#xff0c;剖析系统需求分析&#xff0c;弄明白“做什么”&#xff0c;分析包括业务分析和业务流程的分析以及用例分析&#xff0c;更进一步明确系统的需求。然…

基于微信小程序的书籍阅读系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

STM32F407移植OpenHarmony笔记9

继上一篇笔记&#xff0c;已经完成liteos内核的基本功能适配。 今天尝试启动OHOS和XTS兼容性测试。 如何启动OHOS&#xff1f; OHOS系统初始化接口是OHOS_SystemInit(void)&#xff0c;在内核初始化完成后&#xff0c;就能调用。 extern void OHOS_SystemInit(void); OHOS_Sys…

【数据分享】1929-2023年全球站点的逐年降雪深度数据(Shp\Excel\免费获取)

气象数据是在各项研究中都经常使用的数据&#xff0c;气象指标包括气温、风速、降水、能见度等指标&#xff0c;说到气象数据&#xff0c;最详细的气象数据是具体到气象监测站点的数据&#xff01; 之前我们分享过1929-2023年全球气象站点的逐年平均气温数据、逐年最高气温数据…

「优选算法刷题」:计算布尔二叉树的值

一、题目 给你一棵 完整二叉树 的根&#xff0c;这棵树有以下特征&#xff1a; 叶子节点 要么值为 0 要么值为 1 &#xff0c;其中 0 表示 False &#xff0c;1 表示 True 。非叶子节点 要么值为 2 要么值为 3 &#xff0c;其中 2 表示逻辑或 OR &#xff0c;3 表示逻辑与 AND…

1.0 Hadoop 教程

Hadoop 是一个开源的分布式计算和存储框架&#xff0c;由 Apache 基金会开发和维护。 Hadoop 为庞大的计算机集群提供可靠的、可伸缩的应用层计算和存储支持&#xff0c;它允许使用简单的编程模型跨计算机群集分布式处理大型数据集&#xff0c;并且支持在单台计算机到几千台计…

HBase相关面试准备问题

为什么选择HBase 1、海量存储 Hbase适合存储PB级别的海量数据&#xff0c;在PB级别的数&#xff0c;能在几十到几百毫秒内返回数据。这与Hbase的极易扩展性息息相关。正是因为Hbase良好的扩展性&#xff0c;才为海量数据的存储提供了便利。 2、列式存储 这里的列式存储其实说的…

【Android】RxJava系列01-基本概述和基本用法

少年啊&#xff0c;要永远相信美好的事情即将发生 【Android】RxJava系列01-基本概述和基本用法 1.RxJava的概述2.RxJava的作用3.观察者和被观察者4.背压5.RxJava的基本用法步骤一&#xff0c;创建Observer&#xff08;观察者&#xff09;步骤二&#xff0c;创建Observable&…

LangChain 最近发布的一个重要功能:LangGraph

LangGraph 是 LangChain 最近发布的一个重要功能&#xff0c;LangChain 进入多代理框架领域。通过建立在LangChain 之上&#xff0c;LangGraph 使开发人员可以轻松创建强大的代理运行时。 LangChain 使用其表达语言&#xff08;LCEL&#xff09;为开发人员构建定制链提供技术支…

深度学习(7)---Diffusion Model概念讲解

文章目录 一、基本概括1.1 概念讲解1.2 Denoise模块 二、Stable Diffusion2.1 概念讲解2.2 FID2.3 CLIP 一、基本概括 1.1 概念讲解 1. Diffusion Model是一种生成模型&#xff0c;通过连续添加高斯噪声来破坏训练数据&#xff0c;然后学习反转的去噪过程来恢复数据。它分为正…

go消息队列RabbitMQ - 订阅模式-fanout

1、发布订阅 订阅模式&#xff0c;消息被路由投递给多个队列&#xff0c;一个消息被多个消费者获取。 1&#xff09; 可以有多个消费者 2&#xff09; 每个消费者有自己的queue&#xff08;队列&#xff09; 3&#xff09; 每个队列都要绑定到Exchange&#xff08;交换机&…

【npm】安装全局包,使用时提示:不是内部或外部命令,也不是可运行的程序或批处理文件

问题 如图&#xff0c;明明安装Vue是全局包&#xff0c;但是使用时却提示&#xff1a; 解决办法 使用以下命令任意一种命令查看全局包的配置路径 npm root -g 然后将此路径添加到环境变量中去&#xff0c;这里注意&#xff0c;原本NodeJS的安装路径配置的环境变量不要删除&…

Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(一)

原文&#xff1a;Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 前言 机器学习海啸 2006 年&#xff0c;Geoffrey Hinton 等人发表了一篇论文&#xff0c;展示了如何训练一个能够以最先进的精度…

Tomcat组件架构与数据流

一、背景与简介 Tomcat我们都知道是一个开源的、实现了大部分Java EE、Servlet、JSP规范的Servlet容器, 允许我们将实现了Serlvet接口的Web程序war包进行部署运行。 但是你有对Tomcat做过细致的学习么? 我相信大部分同学和我一样&#xff0c;之前也是只会进行简单使用&#x…

IDEA插件ChatGPT - Easycode安装使用

IDEA插件ChatGPT - Easycode简介 ChatGPT - Easycode 是一个由 OpenAI 开发的 IntelliJ IDEA 插件&#xff0c;它可以利用 ChatGPT 的强大语言生成能力&#xff0c;帮助开发人员提高编码效率。 主要功能&#xff1a; 代码生成&#xff1a;可以根据自然语言描述生成代码&…

sqli-labs-master靶场训练笔记(21-38|精英级)

2024.1.30 level-21 (cookie 注入数据加密) 从页面上就可以看出这次的数据被 baes64 加密了 中国有句古话&#xff1a;师夷长技以制夷 &#xff0c;用base64加密后的数据即可爆出数据 加密前&#xff1a; admin and updatexml(1,concat(~,(select database()),~),1) and …

ReactNative实现宽度变化实现的动画效果

效果如上图所示&#xff0c;通过修改设备宽度实现动画效果 import React, {useRef, useEffect, useState} from react; import {Animated, Text, View, Image} from react-native;const FadeInView props > {const fadeAnim useRef(new Animated.Value(0)).current;React…