JVM2-JVM组成、字节码文件、类的生命周期、类加载器

目录

Java虚拟机的组成

字节码文件

字节码文件打开方式

字节码文件的组成

基本信息

Magic魔数

主副版本号

常量池

字段

方法

属性

字节码常用工具

javap

jclasslib插件

Arthas

类的生命周期

概述

加载阶段

连接阶段

验证

准备

解析

初始化阶段

类加载器

概述

应用场景

类加载器的分类

启动类加载器

扩展类加载器和应用类加载器

扩展类加载器

应用类加载器

JDK9之后的类加载器


Java虚拟机的组成

Java虚拟机主要分为以下几个组成部分:

  • 类加载子系统:核心组件类加载器,负责将字节码文件中的内容加载到内存中
  • 运行时数据区:JVM管理的内存,创建出来的对象、类的信息等内容都会放在这块区域中
  • 执行引擎:包含了即时编译器、解释器、垃圾回收器,执行引擎使用解释器将字节码指令解释成机器码,使用即时编译器优化性能,使用垃圾回收器回收不再使用的对象
  • 本地接口:调用本地使用C/C++编译好的方法,本地方法在Java中声明时,都会带上native关键字,如下图所示

字节码文件

字节码文件打开方式

字节码文件中保存了源代码编译之后的内容,以二进制的方式存储,无法直接用记事本打开阅读

通过NotePad++使用十六进制插件查看class文件:

无法解读出文件里包含的内容,推荐使用 jclasslib工具查看字节码文件

 Github地址: https://github.com/ingokegel/jclasslib

字节码文件的组成

字节码文件总共可以分为以下几个部分:

  • 基础信息:魔数、字节码文件对应的Java版本号、访问标识(public final等等)、父类和接口信息
  • 常量池保存了字符串常量、类或接口名、字段名,主要在字节码指令中使用
  • 字段:当前类或接口声明的字段(变量)信息
  • 方法:当前类或接口声明的方法信息,核心内容为方法的字节码指令
  • 属性:类的属性,比如源码的文件名、内部类的列表等

基本信息

基本信息包含了jclasslib中能看到的两块内容:

Magic魔数

每个Java字节码文件的前四个字节是固定的,用16进制表示就是0xcafebabe

文件是无法通过文件扩展名来确定文件类型的,文件扩展名可以随意修改,不影响文件的内容

软件会使用文件的头几个字节(文件头)去校验文件的类型,如果软件不支持该种类型就会出错

Java字节码文件中,将文件头称为magic魔数

Java虚拟机会校验字节码文件的前四个字节是不是0xcafebabe,如果不是,该字节码文件就无法正常使用,Java虚拟机会抛出对应的错误

主副版本号

主副版本号指的是编译字节码文件时使用的JDK版本号,主版本号用来标识大版本号,JDK1.0-1.1使用了45.0-45.3,JDK1.2是46之后每升级一个大版本就加1;副版本号是当主版本号相同时作为区分不同版本的标识,一般只需要关心主版本号

1.2之后大版本号计算方法就是 : 主版本号 - 44,比如主版本号52就是JDK8

版本号的作用主要是判断当前字节码的版本和运行时的JDK是否兼容。如果使用较低版本的JDK去运行较高版本JDK的字节码文件,无法使用会显示如下错误:

有两种方案:

  1. 升级JDK版本,将图中使用的JDK6升级至JDK8即可正常运行,容易引发其他的兼容性问题,并且需要大量的测试
  2. 将第三方依赖的版本号降低或者更换依赖,以满足JDK版本的要求(建议使用这种方案)

常量池

字节码文件中常量池的作用:避免相同的内容重复定义,节省空间

常量池中的数据都有一个编号,编号从1开始。比如“我爱北京天安门”这个字符串,在常量池中的编号就是7,在字段或者字节码指令中通过编号7可以快速的找到这个字符串

字节码指令中通过编号引用到常量池的过程称之为符号引用

 

字段

字段中存放的是当前类或接口声明的字段信息

如下图中,定义了两个字段a1和a2,这两个字段就会出现在字段这部分内容中,同时还包含字段的名字、描述符(字段的类型)、访问标识(public/private static final等)

方法

字节码中的方法区域是存放字节码指令的核心位置,字节码指令的内容存放在方法的Code属性中

通过分析方法的字节码指令,可以清楚地了解一个方法到底是如何执行的

先来看如下案例:

int i = 0;
int j = i + 1;

这段代码编译成字节码指令之后是如下内容:

要理解这段字节码指令是如何执行的,首先要理解两块内存区域:操作数栈和局部变量表

操作数栈是用来存放临时数据的内容,是一个栈式的结构,先进后出

局部变量是存放方法中的局部变量,包含方法的参数、方法中定义的局部变量,在编译期就已经可以确定方法有多少个局部变量

执行流程:

1.iconst_0,将常量0放入操作数栈。此时栈上只有0

2.istore_1会从操作数栈中,将栈顶的元素弹出来,此时0会被弹出,放入局部变量表的1号位置。局部变量表中的1号位置,在编译时就已经确定是局部变量i使用的位置。完成了对局部变量i的赋值操作

3.iload_1将局部变量表1号位置的数据放入操作数栈中,此时栈中会放入0

4.iconst_1会将常量1放入操作数栈中

5.iadd会将操作数栈顶部的两个数据相加,现在操作数栈上有两个数0和1,相加之后结果为1放入操作数栈中,此时栈上只有一个数也就是相加的结果1

6.istore_2从操作数栈中将1弹出,并放入局部变量表的2号位置,2号位置是j在使用。完成了对局部变量j的赋值操作

7.return语句执行,方法结束并返回

i=i++的执行流程:结果为0

i++的字节码指令如下,其中iinc 1 by 1指令指的是将局部变量表1号位置增加1,其实就实现了i++的操作

i=++i的执行流程:结果为1

面试题:int i = 0; i = i++; 最终i的值是多少?

答:答案是0,通过分析字节码指令发现,i++先把0取出来放入临时的操作数栈中,接下来对i进行加1,i变成了1,最后再将之前保存的临时值0放入i,最后i就变成了0

属性

属性主要指的是类的属性,比如源码的文件名、内部类的列表等

字节码常用工具

javap

javap是JDK自带的反编译工具,可以通过控制台查看字节码文件的内容,适合在服务器上查看字节码文件内容

直接输入javap查看所有参数,输入javap -v 字节码文件名称 查看具体的字节码信息(如果jar包需要先使用 jar –xvf 命令解压)

jclasslib插件

jclasslib也有Idea插件版本,建议开发时使用Idea插件版本,可以在代码编译之后实时看到字节码文件内容

选中要查看的源代码文件,选择 视图(View) - Show Bytecode With Jclasslib,右侧会展示对应源代码编译后的字节码文件内容

tips:

  1. 一定要选择文件再点击视图(view)菜单,否则菜单项不会出现
  2. 文件修改后一定要重新编译之后,再点击刷新按钮

Arthas

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,大大提升线上问题排查效率

官网:https://arthas.aliyun.com/doc/

Arthas的功能列表如下: 

D盘arthas文件夹,使用java -jar arthas-boot.jar 启动程序,输入需要Arthas监控的进程id,输入命令即可使用

命令1:dump

命令详解:https://arthas.aliyun.com/doc/dump.html

dump命令可以将字节码文件保存到本地,如下将java.lang.String 的字节码文件保存到了/tmp/output目录下:

$ dump -d /tmp/output java.lang.StringHASHCODE  CLASSLOADER  LOCATIONnull                   /tmp/output/java/lang/String.class
Affect(row-cnt:1) cost in 138 ms.

命令2:jad

命令详解:https://arthas.aliyun.com/doc/jad.html

jad命令可以将类的字节码文件进行反编译成源代码,用于确认服务器上的字节码文件是否是最新的,如下将demo.MathGame的源代码进行了显示

$ jad --source-only demo.MathGame
/** Decompiled with CFR 0_132.*/
package demo;import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;public class MathGame {private static Random random = new Random();public int illegalArgumentCount = 0;
...

类的生命周期

概述

类的生命周期描述了一个类加载、使用、卸载的整个过程

整体可以分为:

  1. 加载
  2. 连接,其中又分为验证、准备、解析三个子阶段
  3. 初始化
  4. 使用
  5. 卸载

加载阶段

1.加载(Loading)阶段第一步是类加载器根据类的全限定名通过不同的渠道以二进制流的方式获取字节码信息,程序员可以使用Java代码拓展不同的渠道

  • 从本地磁盘上获取文件
  • 运行时通过动态代理生成,比如Spring框架
  • Applet技术通过网络获取字节码文件

2.类加载器在加载完类之后,Java虚拟机会将字节码中的信息保存到内存的方法区中

3.方法区中生成一个InstanceKlass对象,保存类的所有信息,里边还包含实现特定功能比如多态的信息

4.Java虚拟机同时会在堆上生成与方法区中数据类似的java.lang.Class对象,作用是在Java代码中去获取类的信息以及存储静态字段的数据(JDK8及之后)

对于开发者来说,只需要访问堆中的Class对象而不需要访问方法区中所有信息,这样Java虚拟机就能很好地控制开发者访问数据的范围

可以使用JDK自带的hsdb工具查看Java虚拟机内存信息,位于JDK安装目录下lib文件夹中的sa-jdi.jar中,启动命令:java -cp sa-jdi.jar sun.jvm.hotspot.HSDB

连接阶段

连接阶段分为三个子阶段:

  • 验证:验证内容是否满足《Java虚拟机规范》
  • 准备:给静态变量赋初值
  • 解析:将常量池中的符号引用替换成指向内存的直接引用

验证

验证的主要目的是检测Java字节码文件是否遵守了《Java虚拟机规范》中的约束,这个阶段一般不需要程序员参与

主要包含如下四部分,具体详见《Java虚拟机规范》:

  1. 文件格式验证,比如文件是否以0xCAFEBABE开头,主次版本号是否满足当前Java虚拟机版本要求
  2. 元信息验证,例如类必须有父类(super不能为空)
  3. 验证程序执行指令的语义,比如方法内的指令执行中跳转到不正确的位置
  4. 符号引用验证,例如是否访问了其他类中private的方法等

对版本号的验证:编译文件的主版本号不能高于运行环境主版本号,如果主版本号相等,副版本号也不能超过运行环境副版本号

准备

准备阶段为静态变量(static)分配内存并设置初始值

准备阶段只会给静态变量赋初始值,而每一种基本数据类型和引用数据类型都有其初始值

数据类型

初始值

int

0

long

0L

short

0

char

‘\u0000’

byte

0

boolean

false

double

0.0

引用数据类型

null

final修饰的基本数据类型的静态变量,准备阶段直接会将代码中的值进行赋值

解析

解析阶段主要是将常量池中的符号引用替换为直接引用

符号引用是在字节码文件中使用编号来访问常量池中的内容

直接引用不再使用编号,而是使用内存中地址进行访问具体的数据

初始化阶段

初始化阶段会执行静态代码块中的代码,并为静态变量赋值

初始化阶段会执行字节码文件中clinit部分的字节码指令

  • init方法,会在对象初始化时执行
  • main方法,主方法
  • clinit方法,类的初始化阶段执行

字节码指令:

1.iconst_1,将常量1放入操作数栈,此时栈中只有1这个数

2.putstatic指令会将操作数栈上的数弹出来,并放入堆中静态变量的位置,字节码指令中#2指向了常量池中的静态变量value,在解析阶段会被替换成变量的地址

3.后两步操作类似,执行value=2,将堆上的value赋值为2

clinit方法中的执行顺序与Java中编写的顺序是一致的

以下几种方式会导致类的初始化:

  • 访问一个类的静态变量或者静态方法,注意变量是final修饰的并且等号右边是常量不会触发初始化
  • 调用Class.forName(String className)
  • new一个该类的对象时
  • 执行Main方法的当前类

添加 -XX:+TraceClassLoading 参数可以打印出加载并初始化的类

clinit不会执行的几种情况:

1.无静态代码块且无静态变量赋值语句

2.有静态变量的声明,但是没有赋值语句

3.静态变量的定义使用final关键字,这类变量会在准备阶段直接进行初始化

直接访问父类的静态变量,不会触发子类的初始化

子类的初始化clinit调用之前,会先调用父类的clinit初始化方法

面试题1:

以下代码的输出结果是什么?

public class Test1 {public static void main(String[] args) {System.out.println("A");new Test1();new Test1();}public Test1(){System.out.println("B");}{System.out.println("C");}static {System.out.println("D");}
}

执行main方法先初始化Test1的初始化方法,输出结果DA

创建两个对象,会执行两次对象初始化的指令,构造代码块C比构造方法先执行,所以先输出C再输出B

最终结果:DACBCB 

面试题2:

以下代码的输出结果是什么?

public class Demo01 {public static void main(String[] args) {new B02();System.out.println(B02.a);}
}class A02{static int a = 0;static {a = 1;}
}class B02 extends A02{static {a = 2;}
}

分析步骤:

  1. 调用new创建对象,需要初始化B02,优先初始化父类
  2. 执行A02的初始化代码,将a赋值为1
  3. B02初始化,将a赋值为2

如果去掉new B02():

  1. 访问父类的静态变量,只初始化父类
  2. 执行A02的初始化代码,将a赋值为1

练习1:

数组的创建不会导致数组中元素的类进行初始化

public class Test2 {public static void main(String[] args) {Test2_A[] arr = new Test2_A[10];}
}class Test2_A {static {System.out.println("Test2 A的静态代码块运行");}
}

练习2:

final修饰的变量,如果赋值的内容需要执行指令才能得出结果,会执行clinit方法进行初始化

public class Test4 {public static void main(String[] args) {System.out.println(Test4_A.a);}
}class Test4_A {public static final int a = Integer.valueOf(1);static {System.out.println("Test3 A的静态代码块运行");}
}

类加载器

概述

类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术

类加载器只参与加载过程中的字节码获取并加载到内存这一部分

【本地接口JNI是Java Native Interface的缩写,允许Java调用其他语言编写的方法,在hotspot类加载器中,主要用于调用Java虚拟机中的方法,这些方法使用C++编写】

类加载器会通过二进制流的方式获取到字节码文件的内容,接下来将获取到的数据交给Java虚拟机,虚拟机会在方法区和堆上生成对应的对象保存字节码信息

应用场景

1.企业级应用

  • SPI机制
  • 类的热部署
  • Tomcat类的隔离

2.大量的面试题

  • 什么是类的双亲委派机制
  • 打破类的双亲委派机制
  • 自定义类加载器

3.解决线上问题

  • 使用Arthas不停机
  • 解决线上故障

类加载器的分类

类加载器分为两类,一类是Java代码中实现的,一类是Java虚拟机底层源码实现的

  • 虚拟机底层实现:源代码位于Java虚拟机的源码中,实现语言与虚拟机底层语言一致,比如Hotspot使用C++,主要目的是保证Java程序运行中基础类被正确地加载,比如java.lang.String,Java虚拟机需要确保其可靠性
  • JDK中默认提供或者自定义:JDK中默认提供了多种处理不同渠道的类加载器,程序员也可以自己根据需求定制,使用Java语言,所有Java中实现的类加载器都需要继承ClassLoader这个抽象类

类加载器的设计JDK8和8之后的版本差别较大,JDK8及之前的版本中默认的类加载器有如下几种:

类加载器的详细信息可以通过Arthas的classloader命令查看:

classloader - 查看 classloader 的继承树,urls,类加载信息,使用 classloader 去 getResource

  • BootstrapClassLoader是启动类加载器,numberOfInstances是类加载器的数量只有1个,loadedCountTotal是加载类的数量1861个
  • ExtClassLoader是扩展类加载器
  • AppClassLoader是应用程序类加载器

启动类加载器

启动类加载器(Bootstrap ClassLoader)是由Hotspot虚拟机提供的、使用C++编写的类加载器

默认加载Java安装目录/jre/lib下的类文件,比如rt.jar,tools.jar,resources.jar等

/*** 启动程序类加载器案例*/
public class BootstrapClassLoaderDemo {public static void main(String[] args) throws IOException {ClassLoader classLoader = String.class.getClassLoader();System.out.println(classLoader);System.in.read();}
}

这段代码通过String类获取到它的类加载器并且打印,结果是null,这是因为启动类加载器在JDK8中是由C++语言来编写的,在Java代码中去获取既不适合也不安全,所以才返回null 

在Arthas中可以通过 sc -d 类名 的方式查看加载这个类的类加载器详细的信息,比如:

如果用户想扩展一些比较基础的jar包,让启动类加载器加载,有两种途径:

  • 放入jre/lib下进行扩展。不推荐,尽可能不要去更改JDK安装目录中的内容,会出现即使放进去由于文件名不匹配的问题也不会正常地被加载
  • 使用参数进行扩展。推荐,使用 -Xbootclasspath/a:jar包目录/jar包名 进行扩展,参数中的/a代表新增

扩展类加载器和应用类加载器

扩展类加载器和应用程序类加载器都是JDK中提供的、使用Java编写的类加载器

它们的源码都位于sun.misc.Launcher中,是一个静态内部类,继承自URLClassLoader,具备通过目录或者指定jar包将字节码文件加载到内存中

继承关系图:

  • ClassLoader类定义了具体的行为模式,简单来说就是先从本地或者网络获得字节码信息,然后调用虚拟机底层的方法创建方法区和堆上的对象。这样的好处就是让子类只需要去实现如何获取字节码信息这部分代码
  • SecureClassLoader提供了证书机制,提升了安全性
  • URLClassLoader提供了根据URL获取目录下或者指定jar包进行加载,获取字节码的数据
  • 扩展类加载器和应用程序类加载器继承自URLClassLoader,获得了上述的三种能力
扩展类加载器

扩展类加载器(Extension Class Loader)是JDK中提供的、使用Java编写的类加载器

默认加载Java安装目录/jre/lib/ext下的类文件

/*** 扩展类加载器*/
public class ExtClassLoaderDemo {public static void main(String[] args) throws IOException {ClassLoader classLoader = ScriptEnvironment.class.getClassLoader();System.out.println(classLoader);}
}

 

通过扩展类加载器去加载用户jar包:

  • 放入/jre/lib/ext下进行扩展。不推荐,尽可能不要去更改JDK安装目录中的内容
  • 使用参数进行扩展使用参数进行扩展。推荐,使用 -Djava.ext.dirs=jar包目录 进行扩展,这种方式会覆盖掉原始目录,可以用;(windows):(macos/linux)追加上原始目录

应用类加载器

应用程序类加载器会加载classpath下的类文件

默认加载的是项目中的类以及通过maven引入的第三方jar包中的类

/*** 应用程序类加载器案例*/
public class AppClassLoaderDemo {public static void main(String[] args) throws IOException, InterruptedException {//当前项目中创建的Student类Student student = new Student();ClassLoader classLoader = Student.class.getClassLoader();System.out.println(classLoader);//maven依赖中包含的类ClassLoader classLoader1 = FileUtils.class.getClassLoader();System.out.println(classLoader1);Thread.sleep(1000);System.in.read();}
}

 

类加载器的加载路径可以通过 classloader –c hash值 查看:

JDK9之后的类加载器

JDK8及之前的版本中,扩展类加载器和应用程序类加载器的源码位于rt.jar包中的sun.misc.Launcher.java

 

由于JDK9引入了module的概念,类加载器在设计上发生了很多变化

1.启动类加载器使用Java编写,位于jdk.internal.loader.ClassLoaders类中

Java中的BootClassLoader继承自BuiltinClassLoader实现从模块中找到要加载的字节码资源文件

启动类加载器依然无法通过java代码获取到,返回的仍然是null,保持了统一

2、扩展类加载器被替换成了平台类加载器(Platform Class Loader)

平台类加载器遵循模块化方式加载字节码文件,所以继承关系从URLClassLoader变成了BuiltinClassLoader,BuiltinClassLoader实现了从模块中加载字节码文件

平台类加载器的存在更多的是为了与老版本的设计方案兼容,自身没有特殊的逻辑

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

linux 下一跳缓存,early demux(‌早期解复用)‌介绍

3.6版本以后的下一跳缓存 3.6版本移除了FIB查找前的路由缓存。这意味着每一个接收发送的skb现在都必须要进行FIB查找了。这样的好处是现在查找路由的代价变得稳定(consistent)了。3.6版本实际上是将FIB查找缓存到了下一跳(fib_nh)结构上,也就是下一跳缓存下一跳缓存…

ESP32无线WiFi芯片模组,设备物联网连接通信,产品智能化交互升级

在数字化浪潮的推动下,我们正步入一个万物互联的新时代。物联网(IoT)技术,作为连接物理世界与数字世界的桥梁,正逐渐渗透到我们生活的每一个角落。 乐鑫正通过其创新的无线WiFi芯片模组,为这些领域的发展提…

界面控件DevExpress中文教程:如何使用AI扩展Excel计算?

DevExpress WinForms拥有180组件和UI库,能为Windows Forms平台创建具有影响力的业务解决方案。DevExpress WinForms能完美构建流畅、美观且易于使用的应用程序,无论是Office风格的界面,还是分析处理大批量的业务数据,它都能轻松胜…

Elasticsearch的Restful风格API

前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 1、Restful及JSON格式 RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用 XML 格式定义或 JSON 格式定义。R…

STM32CubeMX CAN收发数据

目录 一、CAN总线 1. 差分信号 2. CAN收发器 3. CAN帧结构 4. CAN波特率设置 5. 标识符筛选 二、CubeMX配置 三、Keil代码 一、CAN总线 CAN(Controller Area Network,控制器局域网络)是一种用于车辆、工业自动化等领域的通信协议&…

springboot博客系统

基于springbootvue实现的博客系统 (源码L文ppt)4-031 4 系统设计 博客系统的整体结构设计主要分为两大部分:管理员和博主。他们的权限不同,于是操作功能也有所不同。整体结构设计如图4-2所示。 图4-2 系统结构图 4.3 数据库设…

Unity(2022.3.41LTS) - 角色控制器和3D刚体

目录 一. 角色控制 二. 3D刚体 一. 角色控制 名称:功能:坡度限制将碰撞器限制为仅爬升比指示值更陡峭(以度为单位)的斜坡。步长偏移只有当楼梯离地面比指示值更近时,角色才会爬上楼梯。此值不应大于 Character Contr…

《CounTR: Transformer-based Generalised Visual Counting》CVPR2023

摘要 本论文考虑了通用视觉对象计数问题,目标是开发一个计算模型,用于计算任意语义类别的对象数量,使用任意数量的“样本”(即可能为零样本或少样本计数)。作者提出了一个新颖的基于Transformer的架构,称为…

shell 学习笔记:变量、字符串、注释

目录 1. 变量 1.1 定义使用变量 1.2 变量命名规则 1.3 只读变量 1.4 删除变量 1.5 变量类型 1.5.1 字符串变量 1.5.2 整数变量 1.5.3 数组变量 1.5.3.1 整数索引数组 1.5.3.2 关联数组 1.4 环境变量 1.5 特殊变量 2. 字符串 2.1 单引号字符串 2.2 双引…

【32项目】基于stm32f103c8t6WIFI远程监控智慧农业大棚(含完整代码)

目录 前言 设计背景 设计原理 所需材料 JW01二氧化碳传感器介绍 YL-69土壤湿度传感器介绍 PCB及原理图 部分代码(完整代码见文章末尾) 前言 随着农业现代化的发展,智慧农业的概念越来越受到重视。智慧农业利用物联网、大数据、人工智…

计算机网络 数据链路层2

ALOHA:想发就发 CSMA 载波监听多路访问协议 CS:载波监听,在发送数据之前检测总线上是否有其他计算机在发送数据 1-坚持CSMA:主机想发送消息,需要监听信道; 信道空闲则直接传输信息; 信道忙碌则一直监听,直…

【JavaWeb】JDBCDruidTomcat入门使用

本章使用技术版本: Tomcatv10.1.25 关于javaweb相关的其他技术,比如tomcat和maven,在我的主页记录了笔记,ajax我用的是本地笔记以后再考虑上传,前端三板斧我用的菜鸟教程文档 JDBC 初识 JDBC概念 JDBC 就是使用Jav…

【深度学习 transformer】使用pytorch 训练transformer 模型,hugginface 来啦

Hugging Face是一个致力于开源自然语言处理(NLP)和机器学习项目的社区。它由几个关键组件组成: Transformers:这是一个基于PyTorch的库,提供了各种预训练的NLP模型,如BERT、GPT、RoBERTa、DistilBERT等。它…

SEO之网站结构优化(十四-内部链接及权重分配3)

初创企业搭建网站的朋友看1号文章;想学习云计算,怎么入门看2号文章谢谢支持: 1、我给不会敲代码又想搭建网站的人建议 2、“新手上云”能够为你开启探索云世界的第一步 博客:阿幸SEO~探索搜索排名之道 7、锚文字分布及变化 前面…

WebGIS与WebGL是什么,两者之间的关系?

WebGL和 WebGlS 都是 web 技术领域的重要内容,特别是这几年webgis开发领域,和webgl打交道是必然的,常见的WebGL开发的基础上,比如二维的Leaflet、三维的Cesium也都是热门。 WebGL是一种基于 HTML5 Canvas 元素的 JavaScriptAPI&a…

RLHF(带有人类反馈的强化学习)初探

我的目标是,在决策游戏上学习某人风格,可以让人对战“带有某人风格”的AI,比如你可以在这里对战“sky风格的AI”,这样的效果。 我最开始受到的启发来源于xbox的广告《爸爸的幽灵车》,已故人在游戏中留下的速度记录的固定轨迹。 …

IOS17.0安装巨魔:TrollRestore巨魔发布

👻 TrollRestore 17.0 巨魔发布 15.0 - 16.7 RC(20H18)和17.0。 官网:https://trollrestore.com/ 下载:https://pan.metanetdisk.com/IOS/%E5%B7%A8%E9%AD%94%E7%8E%A9%E5%AE%B6/TrollRestore.com 使用:ht…

【数字人】Facevid2vid:用于视频会议的一次性自由视图说话头合成

论文:https://arxiv.org/pdf/2011.15126 github:GitHub - zhanglonghao1992/One-Shot_Free-View_Neural_Talking_Head_Synthesis: Pytorch implementation of paper "One-Shot Free-View Neural Talking-Head Synthesis for Video Conferencing" 一种新颖…

缓存分布式一致性问题

缓存一致性问题发生的原因,是在更新数据时数据库和缓存数据的不一致。我们要做到保证缓存的最终一致性。如果数据需要强一致性建议直接查询数据库。 双写模式 双写模式为先写数据库,在写缓存。 进来两个请求,先执行“请求1”的操作写入数据…

装WebVideoCreator记录

背景,需要在docker容器内配置WebVideoCreator环境,配置npm、node.js https://github.com/Vinlic/WebVideoCreatorWebVideoCreator地址:https://github.com/Vinlic/WebVideoCreator 配置环境,使用这个教程: linux下安…