16-01、JVM系列之:内存与垃圾回收篇(一)
JVM系列之:内存与垃圾回收篇(一)
##本篇内容概述:
1、JVM结构
2、类加载子系统
3、运行时数据区之:PC寄存器、Java栈、本地方法栈
一、JVM与JAVA体系结构
JAVA虚拟机与JAVA语言并没有必然的联系,它只是与特定的二进制文件格式:Class文件格式关联,Class文件中包含了JAVA虚拟机指令集(字节码、Bytecodes)和符号表,还有一些其他辅助信息。
1、JVM整体结构
【说明:线程共享->方法区和堆; 各个线程独享:Java栈、本地方法栈和程序计数器】
【程序计数器Program Counter Register即:PC寄存器】
2、JAVA代码执行流程
3、JVM的架构模型
java编译器输入的指令流基本上基于以下两种指令集架构:
- 基于栈的指令集架构(基于零地址指令,出栈入栈,指令集小,但指令多。不依赖于硬件可移植性好)
- 基于寄存器的指令集架构(以一地址、二地址、三地址指令为主,依赖于硬件可移植性差,但执行效率更高)
总结:
由于跨平台性的设计,Java的指令都是根据栈来设计的。不同的平台CPU架构不同,所以不能设计为基于寄存器的。有点事跨平台,指令集小,编译器容易实现,
缺点是性能下降,实现同样的功能需要更多的指令。
4、JVM发展
虚拟机 | 特点 |
---|---|
SUN Classic VM | 只有解释器,没有JIT编译器 |
Exact VM | 编译器+解释器 混合工作模式 |
HotSpot(Oracle) | 引入了方法区Method Area。 热点代码探测+编译器解释器协同工作 |
JRockit(Oracle) | 专注于服务器端,JRockit不包含解释器实现,全部靠即时编译器编译后执行 |
J9(IBM) |
二、类加载子系统
- 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识;
- ClassLoader只负责class文件的加载,至于它是否可以运行,则有执行引擎Execution Engine决定;
- 加载的类信息存放于一块称谓方法区Method Area的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)
类加载器子系统分为三个阶段:加载阶段、链接阶段、初始化阶段
【class file加载到 JVM 中,被称为DNA元数据模板,放在方法区。】
.class文件 => JVM => 最终成为元数据模板,此过程就要一个运输工具(类装载器Class Loader)。
类的加载过程:
1、加载Loading
##加载
1、通过一个类的全限定名获取定义此类的二进制字节流(从本地、网络、ZIP、动态代理、加密文件中等)2、将这个字节流锁代表的静态存储结构转化为方法区的运行时数据结构3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
类加载器:
JVM支持两种类型的类加载器:引导类加载器(Bootstrap ClassLoader)自定义类加载器(User-Defined ClassLoader)==>派生于抽象类ClassLoader的类加载器都是自定义类加载器extention classloader和application classloader都是自定义类加载器Bootstrap ClassLoader是C/C++编写的,其他类加载器是java编写的他们是包含关系:其他自定义类加载器< Application classloader < extention classloader < bootstap classloaderJava的核心类库都是使用bootstrap classloader加载的
1.1、引导类加载器Bootstrap ClassLoader (启动类加载器)
- 这个类加载使用C/C++语言实现,嵌套在JVM内部
- 它用来加载java的核心类库,用于提供JVM自身需要的类
- 加载extention classloader和application classloader,并指定为它们的父类加载器
- 出于安全考虑,Bootstrap加载器只加载包名为java、javax、sun等开头的类
- 它不继承自java.lang.ClassLoader,没有父加载器
1.2、扩展类加载器Extension ClassLoader
- 使用java编写,由sun.misc.Launcher$ExtClassLoader实现
- 派生于ClassLoader类
- 父类加载器为Bootstrap ClassLoader
- 从java.ext.dir系统属性所指定的目录或JDK的安装目录的jre/lib/ext子目录下加载类库,如果用户自定义创建的JAR包放在此目录下,也会由扩展类自动加载
1.3、应用程序类加载器Application ClassLoader(系统类加载器)
- java语言编写,由sun.misc.Launcher$AppClassLoader实现
- 派生于ClassLoader类
- 父类加载器为Extension ClassLoader
- 负责加载环境变量classpath或系统属性java.class.path指定路径下的类
- 它是程序中默认的类加载器,一般java应用的类都是由它来完成加载
- 通过ClassLoader.getSystemClassLoader()方法来获取该加载器
【java的日常应用开发中,类的加载几乎都是由上述3种类加载器配合执行的】
演示:
public class ClassLoaderTest {public static void main(String[] args) {//获取系统类加载器ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2//获取扩展类加载器ClassLoader extentionClassLoader = systemClassLoader.getParent();System.out.println(extentionClassLoader);//sun.misc.Launcher$ExtClassLoader@77459877//获取引导类加载器ClassLoader bootstrapClassLoader = extentionClassLoader.getParent();System.out.println(bootstrapClassLoader);//null//获取自定义的ClassLoaderTest类的类加载器//说明:对于用户自定义的类默认使用 系统类加载器ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2//获取String的类加载器//Java的核心类库都是使用bootstrap classloader加载的ClassLoader stringClassLoader = String.class.getClassLoader();System.out.println(stringClassLoader);//null}
}
自定义自己的类加载器,可以继承ClassLoader或者URLClassLoader类 重写findClass()方法即可
1.4、双亲委派机制
JAVA虚拟机对class文件采用的是"按需加载"的方式,也就是说当需要使用该类的时候才会将他的class文件加载到内存中生成class对象。而且加载某个类的class文件时,java虚拟机采用的是"双亲委派模式",即把请求交由父类处理,也是一种任务委派模式
##工作原理:
1、如果一个类加载器收到了类加载的请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;2、如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;3、如果父类加载器可以完成累的加载任务,就成功返回,若父类加载器无法完成此家在任务,子加载器才会
尝试自己去加载,这就是双亲委派模式##作用:
【只有这样如果我们自定义的java.lang.String类才不会被执行,也就不会覆盖系统自带的String了】
1、避免了类的重复加载
2、保护了程序安全,防止核心API被恶意篡改
1.5、沙箱安全机制
如上,我们自定义java.lang.String类,但是在加载自定义String类的时候会率先使用引导类加载器加载。
从而引导类加载器在加载的过程中会先加载JDK自带的文件(rt.jar包下的java.lang.String.class)。这样可以保证对java核心源代码的保护,这就是"沙箱安全机制"。
1.6、其他
1、在JVM中表示两个class对象是否为同一个类,有两个条件:a、类的完整类名必须一致,包括包名b、加载这个类的ClassLoader类加载器必须相同2、JVM必须知道一个类型是由启动类加载器加载的还是用户自定义类加载器加载的。
如果一个类型是由用户自定义类加载器加载的,那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。
当解析一个类型到另一个类型的引用的时候,JVM需要保证这两个类型的类加载器是相同的。3、JAVA程序对类的使用分为:主动使用 和 被动使用
主动使用,分为7种情况
a、创建类的实例
b、访问某个类或接口的静态变量,或者对该静态变量赋值
c、调用类的静态方法
d、反射(如:Class.forName("com.lee.MyTest"))
e、初始化一个类的自雷
f、Java虚拟机启动的时候被标明为启动类的类
g、JDK7开始提供的动态语言支持除了以上情况,其他使用Java类的方式都被看做是队类的被动使用。主动使用会导致类的Initailization初始化(即下面的初始化),被动使用不会
2、链接Liking
##验证verify文件格式验证、元数据验证、字节码验证、符号引用验证(保证类加载的正确性)[.class文件一般以 CA FE BA BE开头]##准备Prepare为类"变量"分配内存并设置该类变量的默认初始值,即零值。(private static int a = 1;在prepare环节a = 0。在initial环节才被赋值为1)这里不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显示初始化。(private final static int a = 1;在prepare环节a = 1)这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到JAVA堆中。##解析Resolve将常量池内的符号引用转换为直接引用的过程
3、初始化Initailization
##初始化
初始化阶段就是执行类构造器方法<clinit>()的过程【<clinit>()不同于类的构造器<init>()】此方法不需要定义,是javac编译器自动收集类中的所有 "类变量的赋值动作(static?)" 和 "静态代码块中的语句" 合并而来构造方法中指令按语句在源文件中出现的顺序执行若该类具有父类,JVM会保证子类<clinit>()执行前父类<clinit>()已经执行完毕虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁(一个类只能被执行一次<clinit>())##解释
类变量与实例变量的区别:1)类变量属于类,可以共享,属于公共属性;实例变量属于某个对象个体;2)加上static 为类变量或者静态变量,否则为实例变量;
按顺序执行
public class ClassInitTest{static{number = 20;//System.out.println(number);//这里可以赋值,但不可以调用-》非法前向引用}private static int number=10;//在linking的prepare:number=0 ---> initail:按顺序执行 20 ->10public static void main(String[] args){System.out.println(number);//number的值为10}}
加载子类()执行前必须保证父类()的执行
public class ClinitTest1{static class Father{public static int A = 1;static{A=2;}}static class Son extends Father{public static int B = A;}public static void main(String[] args){//先加载Father类,prepare:A=0 initial A=1 然后A=2//再加载Son类,prepare:B=0 initail B=A=2System.out.println(Son.B);//2}
}
三、运行时数据区
1、运行时数据区概述及线程
Java虚拟机定义了若干种程序运行期间会使用到的运行时数据区,其中有些会随着虚拟机的启动二创建,随着虚拟机的退出而销毁。另外一些则是与线程一一对应,这些与线程对应的数据区域会随着线程的开始和结束而创建和销毁。##上图:
Method Area方法区 + Heap Area堆空间 是共享的
Stack Area + PC Registers + Native Method stack是各个线程独享的1)、每个线程:独立使用程序计数器、栈、本地栈。
2)、线程间共享:堆、堆外内存(永久代或元空间、代码缓存)Method Area方法区 在 JDK8 之后 成了 元空间,使用的是本地内存
关于线程间共享的说明:
Class Runtime (java.lang.Runtime)每个JVM只有一个Runtime实例。即为运行时环境(Run data Area 运行时数据区)
[所以Runtime是单例的]
线程:
线程(Thread)是一个程序里的运行单元。JVM允许一个应用程序有多个线程并行执行。在Hotspot JVM里,每个线程都与操作系统的本地线程直接映射。
(当一个Java线程准备好执行以后,此时操作系统的本地线程也同时创建。Java线程执行终止后,本地线程也会被回收)操作系统负责所有线程的安排调度到任何一个可用的CPU上,一旦本地线程初始化成功,他就会调用Java线程的run()方法##Hotspot JVM里主要有如下几种线程:·虚 拟 机 线 程·周 期 任 务 线 程·GC 线 程·编 译 线 程·信 号 调 度 线 程
2、PC寄存器
没有GC垃圾回收,不会报OOM(OutOfMemoryError)
##PC寄存器 :Program Counter Register 【类似一个游标】
(JVM中的PC寄存器是对屋里PC寄存器的一种抽象模拟,也可以成为程序的钩子,程序的行号指示器)##作用:
PC寄存器用来存储指向下一条指令的地址,也就是即将要执行的指令代码。
由执行引擎读取下一条指令##特点:
·它是一块很小的内存空间,也是运行速度最快的存储区域·JVM规范中:每个线程都有它自己的PC寄存器,是线程私有的,生命周期与线程保持一致·任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。PC寄存器会存储当前线程
正在执行的Java方法的JVM指令地址,或者,如果是在执行native方法,则是未指定值[undefined]下面的 栈帧 也就是 方法,当前栈帧就是程序中的当前Java方法
·PC寄存器是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。·字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。·它是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域
javap -v PCRegister.class下方右侧 0 2 3···即 指令地址 或 偏移地址
常见问题:
一、##使用PC寄存器存储字节码指令地址有什么用?##为什么使用PC寄存器记录当前线程的执行地址?因为CPU需要不停的快速切换各个线程,切换回来后,就得以知道接着从哪开始继续执行了。JVM的字节码解释器就需要通过改变PC寄存器的值来明确下一条应该执行什么样的字节码指令。二、##PC寄存器为什么被设定为线程私有的?所谓的多线程在特定的时间段内只会执行其中的某一个线程,CPU会不停地做快速切换而已。
这样必然导致线程的经常中断或恢复。为了能够准确地记录各个线程正在执行的当前字节码指令地址,最好的办法自然是为每个线程都
分配一个PC寄存器,这样一来各个线程之间变可以进行独立计算,从而不会出现相互干扰的情况。
拓展:CPU时间片:
CPU时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它为时间片。宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。
微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行
并行: 多个任务 被多个CPU同时执行,相对的就是 "串行"并发:多个任务 被一个CPU快速切换的交替执行
3、虚拟机栈(Java栈)
3.1、概述
没有GC垃圾回收问题,但会存在OOM(OutOfMemoryError)的问题
一、##出现背景:前面在JVM架构模型中讲到,指令流基本上基于两种指令集架构:基于栈的指令集架构、基于寄存器的指令集架构。由于跨平台性的设计Java的指令都是根据栈来设计的。(不同平台的CPU架构不同,所以不能使用基于寄存器的)。优点是:跨平台,指令集小,编译器容易实现。
缺点是:性能下降,实现同样的功能需要更多的指令(指令集小但指令多)二、##内存中栈与堆的区别:栈是运行时的单位,堆是存储的单位即:栈解决程序的运行问题,即程序如何执行或者说如何处理数据。
堆解决的是数据存储的问题,即数据怎么放,放哪儿。三、##Java虚拟机栈是什么?Java虚拟机栈,早期也叫Java栈。
每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),每一个栈帧对应着一个的Java方法调用。
一个个方法的调用对应着一个个栈帧的入栈和出栈操作。Java栈是线程私有的。其生命周期和线程一致。主管Java程序的运行,它保存 方法的 "局部变量(8种基本数据类型、对象的引用地址)"、 "部分结果",并参与方法的调用和返回。 四、##栈的有点:栈是一种快速有效的分配存储方式,访问速度仅次于程序计数器。JVM直接对Java栈的操作只有两个:
·方法的执行,伴随着进栈(入栈、压栈)
·执行结束后的 出栈 工作对于栈来说不存在GC垃圾回收问题
五、##栈中可能出现的异常:Java虚拟机规范允许Java栈的大小是 "动态的" 或者 "固定不变"的。 ·如果采用固定大小的Java虚拟机栈,那每一个线程的Java虚拟机栈容量可以在线程创建的时候独立选定。
如果线程请求分配的栈内容超过Java虚拟机允许的最大容量,Java虚拟机将会抛出一个StackOverflowError 异常【递归的时候可能会出现】·如果Java虚拟机栈可以动态扩展,并且在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将会抛出一个OutOfMemoryError异常六、##设置栈的大小
【java -Xss256k 设置stack大小】
【java -Xmx512m -Xmx512m 其中-Xmx设置堆最大值 -Xms设置堆初始值。】
3.2、栈的存储结构和运行原理
一、##栈的存储单位每个线程都有自己的栈,栈中的数据都是以栈帧Stack Frame的格式存在。在这个线程上正在执行的每一个方法都各自对应一个栈帧Stack Frame。栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息。二、##栈运行原理JVM直接对Java栈的操作只有两个,就是对栈帧的“压栈”和“出栈”,遵循“先进后出”、“后进先出”的原则。在一条活动线程中,一个时间点上,只会有一个活动的栈帧,即只有当前正在执行的方法的栈帧
(栈顶栈帧)是有效的,这个栈帧被称谓当前栈帧Current Frame,与当前栈帧相对应的方法
就是当前方法Current Method,定义这个方法的类就是当前类Current Class.执行引擎运行的所有字节码指令只针对当前栈帧进行操作。如果在该方法中调用了其他方法,对应的新的栈帧就会被创建出来,放在栈的顶端,称谓新的当前栈帧。不同线程中锁包含的栈帧是不允许存在相互引用的,即不可能在一个栈帧中引用另外一个线程的栈帧。如果当前方法调用了其他方法,方法返回之际,当前栈帧会传回此方法的执行结果给前一个栈
帧,接着,虚拟机会丢弃当前栈帧,使得前一个栈帧重新成为当前栈帧。Java方法有两种返回函数的方式:一种是正常的函数返回,使用return指令;另一种是抛异常。
不管使用哪种方式,都会导致栈帧被弹出。
3.3、栈帧的内部结构
一、##栈帧的内部结构每个栈帧中存储着:
·局部变量 Local Variables
·操作数栈 Operand Stack (或表达式)
·动态链接 Dynamic Linking (或指向运行时常量池的方法引用)
·方法返回地址 Return Address (或方法正常退出 或者异常退出的定义)
·一些附加信息
1>、局部变量表Local Variables
·局部变量表也被称谓 局部变量数组 或 本地变量表·定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,
这些数据类型包括8种基本数据类型、对象引用(refrence),以及returnAddress类型·由于局部变量表是建立在线程的栈上,是线程的私有数据,因此不存在数据的安全问题·局部变量表所需的容量大小是在编译器确定下来的,并保存在方法的Code属性的maximum local variable是数据项中。
在方法运行期间是不会改变局部变量表的大小的。
【上图 LineNumberTable表示 字节码的行号 和 java代码行号对应的关系】·方法嵌套调用的次数由栈的大小决定(如递归)。·局部变量表中的变量只在当前方法调用中有效。方法执行时,虚拟机通过使用局部变量表完成参数值
到参数变量列表的传递过程。当方法调用结束后,随着方法栈帧的销毁,局部变量表也会随之销毁。【局部变量表是主要影响栈帧大小的】##上图中LocalVariableTable中Slot的理解·参数值的存放总是在局部变量数组的index0开始,到数组长度的-1的索引结束。·局部变量表,最基本的存储单元是Slot 变量槽·局部变量表中存放编译期可知的8中基本数据类型,引用类型,returnAddress类型的变量·局部变量表里,32位以内的类型只占用一个slot(包括returnAddress类型)
64位的类型(long和double)占用两个slot
> byte\short\char在存储前被转换为int
> boolean也被转换为int 0表示false,非0表示true
> long和double则占据两个slotbyte\short\char\float\int 占1个slot
long\double占2个slot·JVM回味局部变量表中每个slot都分配一个访问索引,通过这个索引即可成功访问到局部变量表中指定的局部变量值。·当一个实例方法被调用的时候,他的方法参数和方法体内部定义的局部变量将会按照顺序被赋值到局部变量表中的每一个slot上。·如果需要访问局部变量表中的一个64bit的局部变量值时,只需要使用前一个索引即可。(long和double)·如果当前栈帧是由构造方法或者实例方法创建的,那么该对象引用this将会存放在index为0的slot处,其余的参数按照参数表顺序继续排列。
(因此被static修饰的方法中是不能够使用this的)·栈帧中的局部变量表中的槽位slot是可以重复利用的,如果一个局部变量过了其作用域,那么在其作用域之后
声明的新的局部变量就很有可能会服用过期局部变量的槽位,从而达到节省资源的目的。
例如:
public void test{int a = 0;{int b = 0;b = a + 1;}int c = a +1;
}
其中b的作用域在其{}内,变量c是使用之前已经销毁的变量b占据的slot的位置。
变量 f 的位置被重复利用了
##拓展变量的分类:按照数据类型分:①基本数据类型 ②引用数据类型按照在类中的声明位置分:①成员变量:类变量(或叫静态变量,被static修饰)实例变量②局部变量
##类变量linking的prepare阶段:给类变量默认赋值-->initial阶段:给类变量显式赋值即静态代码块赋值##实例变量随着对象的创建,会在堆空间中分配实例变量空间,并进行默认赋值
##局部变量在使用前,必须要进行显式赋值!否则,编译不通过
##补充说明:·在栈帧中,与性能调优关系最为模切的部分就是 局部变量表。
在方法执行时,虚拟机使用局部变量表完成方法的传递。·局部变量表中的变量也是重要的垃圾回收根节点,只要被局部变量表中引用数据类型直接或间接引用的对象都不会被回收。(局部变量表中 存储:基本数据类型 和 引用数据类型,引用数据类型引用的是堆空间中的对象,所以影响了堆空间的GC垃圾回收。如果局部变量表中的引用数据类型不存在了,那么堆空间中的数据可能就会被回收)
2>、操作数栈Operand Stack
##操作数栈:·后进先出,也被称之为表达式栈·操作数栈,主要是用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间·操作数栈,在方法执行的过程中,根据字节码指令,往栈中写入数据或提取数据,即入栈PUSH/出栈POP
(有些字节码指令是将值PUSH进操作栈,有些字节码指令是将操作数POP出栈,使用后再把结果PUSH进栈)
(比如执行赋值、交换、求和等操作)
·操作数栈,主要是用于保存计算过程中的中间结果,同时作为计算过程中变量临时的存储空间。·操作数栈就是JVM执行引擎的一个工作区,当一个方法开始执行的时候,一个新的栈帧也会随
之被创建出来,这个方法的操作数栈是空的。·每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译期就定义
好了。保存在方法的code属性中,为max_stack的值。·栈中的任何一个元素都是可以任意的Java数据类型:>32bit的类型占用一个栈单位深度>64bit的类型占用两个栈单位深度·操作数栈并非采用访问索引的方式来进行数据访问的,而是只能通过标准的入栈和川站操作来完成一次数据访问·如果被调用的方法带返回值的话,其返回值将会被压入当前栈帧的操作数栈中。并更新PC寄存器中下一条需要执行的字节码指令。
(当当前方法用到上一条方法的返回值时会从操作数栈中load)·操作数栈中元素的数据类型必须与字节码指令的序列严格匹配,这由编译器在编译器期间进行
验证,同时在类家在过程中的类检验阶段的数据流分析阶段要再次验证。·另外,我们说Java虚拟机的解释引擎就是基于栈的执行引擎,其中的栈值得就是操作数栈。
(也就是说 JVM的执行引擎和操作数栈 配合操作的)
栈顶缓存技术Top-Of-Stack Cashing
·由于操作数是存储在内存中的,因此频繁第执行内存读/写操作必然会影响执行速度。为了解决这个问题,HotSpot JVM的设计者提出了栈顶缓存技术。即将栈顶元素全部缓存在物理CPU的寄存器中,以此降低对内存的读/写次数,提升执行殷勤的执行效率。
3>、动态链接Dynamic Linking
动态链接:指向运行时常量池的方法引用。
(运行时常量池 在 "方法区" 中)·每一个栈帧内部都包含一个执行运行时常量池中该栈帧所属方法的引用。
包含这个引用的目的就是为了支持当前方法的代码能够实现动态链接:invokedynamic指令·在Java源文件被编译到字节码文件中时,所有的变量和方法引用都作为符号引用保存在class文件的常量池里。
(比如描述一个方法调用了另外的其他方法时,就是通过常量池中指向方法的符号引用来表示的)·动态链接的作用就是为了将这些符号引用转换为调用方法的直接引用
·符号引用:以一组符号来描述所引用的目标 符号可以是任何形式的字面量,只要使用时能够无歧义的定位到目标即可
如:
public static void main(java.lang.String[]);....Code:stack=2, locals=3, args_size=1....11: invokevirtual #4 // Method methods1:()I·直接引用:直接引用可以是直接指向目标的指针、相对偏移量、一个能间接定位到目标的句柄
如:
Constant pool:....#4 = Methodref #2.#39 // com/lee/jvm/rundataarea/LocalVariablesTest.methods1:()I##JVM指令拓展:
普通调用指令:
invokestatic:调用静态方法
invokespecial:调用<init>方法、私有及父类方法
invokevirtual:调用所有虚方法
invokeinterface:调用接口方法
动态调用指令:
invokedynamic:动态解析出需要调用的方法,然后执行
4>、方法返回地址 Return Address
·方法返回地址:存放调用该方法的PC寄存器的值·一个方法的结束,有两种方式:
>正常执行完成
>出现未处理异常,非正常退出·无论哪种方式退出,在方法退出后都返回到该方法被调用的位置。
方法正常退出是,调用者的PC计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。
而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。
1、执行引擎遇到任意一个方法返回的字节码指令return,会有返回值传递给上层的方法调用这,简称正常完成出口:
>一个方法在正常调用完成之后究竟需要使用哪一个返回指令还需要根据方法返回值的实际数据类型而定。
>在字节码指令中,返回指令包含为ireturn(当返回值时boolean\byte\chat\short\int类型时使用)、lreturn(long)、freturn(float)、dreturn(double)以及areturn
另外还有一个return指令提供声明为void的方法、实力初始化方法、类和接口的初始化方法使用。2、在方法执行的过程中遇到异常Exception,并且这个异常没有在方法内处理,也就是只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出。简称异常完成出口方法执行过程中抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码。##本质上,方法的退出就是当前栈帧出栈的过程。
此时需要恢复上层方法的局部变量表、操作数栈、将返回值亚茹调用者栈帧的操作数栈、设置PC寄存器值等,让调用者方法继续执行下去。正常完成出口和异常完成出口的区别在于:通过异常完成的出口退出的不会给他的上层调用者产生任何的返回值。
5>、一些附加信息
栈帧中还允许携带与Java虚拟机实现相关的一些附加信息。如:对程序调试提供支持的信息
6>、关于Java栈的思考
一、##举例栈溢出的情况?如果设置了栈的大小,栈空间固定,虚拟机栈增加栈帧溢出会出现StackOverflowError
如果没有设置了栈的大小,虚拟机栈增加栈帧内存溢出或者增加新的线程时会出现OutOfMemeryError二、##调整栈的大小,就能保证不出现溢出吗?不能保证,只能保证栈溢出的阈值增大而已。三、##分配的栈内存越大越好吗?不是,内存空间固定,某个栈空间变大了,挤占了线程数或者其他结构的内存空间四、##垃圾回收是否会涉及到虚拟机栈?不会,栈的生命周期是随 线程 的创建和销毁的,所以不存在GC垃圾回收,如果溢出直接报错五、##方法中定义的局部变量是否线程安全?不一定,举例说明
public static void method() {int x = 0;for(int i = 1;i<=10;i++) {x *= i;}System.out.println(x);
}
上面线程安全的,当多个线程同时执行此方法时,每个线程都会产生一个自己的变量x,每个线程之间互不干扰,不会对其他线程的变量x有影响。public static void method(StringBuilder sb) {sb.append(1);sb.append(2);System.out.println(sb.toString());
}
上面线程不安全,在这里sb对象是作为参数传入的,这意味着它并不是线程私有的,多个线程都可以同时访问它。所以不是线程安全的。 public static StringBuilder method() {StringBuilder sb = new StringBuilder();sb.append(1);sb.append(2);return sb;
}
上面线程不安全,虽然sb对象是在方法内生成的对象。但是sb作为一个返回变量返回,其他线程可以去拿取它,对它进行并发的操作。 public static String method() {StringBuilder sb = new StringBuilder();sb.append(1);sb.append(2);return sb.toString();
}
上面线程安全,sb消亡了,sb.toString()相当于我们又new 了一个string,这个new出来的string可能不安全,但是方法内的sb是安全的。
4、本地方法栈
在写本地方法栈前 先 了解一下本地方法库和本地方法接口
##本地方法本地方法就是,一个Native Method就是一个Java调用一个本地非Java代码的接口,比如是C或者C++。(即一个Java方法其方法内部是由非Java语言实现的)
如:public final native Class<?> getClass();
如:Thread 中 private native void start0();标识符native可以与所有其他的java标识符连用,但是abstract除外
本地方法栈:
·Java虚拟机栈用于管理Java方法的调用。而本地房发展用于管理本地方法的调用。·本地方法栈,也是线程私有的·允许被是线程固定或者是可动态扩展的内存大小(在内存溢出方面和虚拟机栈是相同的)·本地方法时C语言实现的·具体做法是 虚拟机栈 中登记native方法,在 执行引擎执行时加载本地方法库。
相关文章:
16-01、JVM系列之:内存与垃圾回收篇(一)
JVM系列之:内存与垃圾回收篇(一) ##本篇内容概述: 1、JVM结构 2、类加载子系统 3、运行时数据区之:PC寄存器、Java栈、本地方法栈一、JVM与JAVA体系结构 JAVA虚拟机与JAVA语言并没有必然的联系,它只是与特…...
经典蓝牙(BT/EDR)蓝牙配对与连接
经典蓝牙的连接过程包括跳频,扫描,配置交换等过程。对ACL链路以及sco的连接过程也做详细的分析。 1. 为什么不配对便无法建立连接? 任何无线通信技术都存在被监听和破解的可能,蓝牙SIG为了保证蓝牙通信的安全性,采用…...
用 Python 从零开始创建神经网络(十四):L1 和 L2 正则化(L1 and L2 Regularization)
L1 和 L2 正则化(L1 and L2 Regularization) 引言1. Forward Pass2. Backward pass到此为止的全部代码: 引言 正则化方法旨在降低泛化误差。我们首先讨论的正则化形式是L1正则化和L2正则化。L1和L2正则化用于计算一个数值(称为惩…...
特殊的数学性质
一个数模9的结果等于它的每一位数相加和模9...
最长递增子序列&什么是继承性?C++中如何实现继承?继承的好处和注意事项有哪些?
最长递增子序列 方法一:暴力二维dp,初始状态:每个元素至少和自己构成一个上升序列,大小为1,状态转移:找到前面结尾数字小于当前数组元素的最长序列,当前位置的长度就是lenpre1. class Solutio…...
汽车IVI中控开发入门及进阶(三十五):架构QML App Architecture Best Practices
在Qt/QML工程的架构中,架构很重要,虽然本身它有分层,比如QML调用资源文件(图片等)显示GUI界面,后面的CPP文件实现界面逻辑,但是这个分类还有点粗。在实际开发中,界面逻辑也就是基于类cpp的实现,也开始使用各种面向对象的设计模式,实现更加优秀的开发架构,这点尤其在…...
面试题整理(二)
芯冰乐知识星球入口:芯冰乐...
编码及其代码
编码 形成文字所需bit点------有相应代号(编码---有好多种8/16/24/32)都已提前形成好,放哪哪就会形成那个文字 同一个文字在不同编码存的码不一样 用一种编码存的话,如果用另一种编码解析就会出现乱码 Windows默认编码为ANSI …...
python selenium(4+)+chromedriver最新版 定位爬取嵌套shadow-root(open)中内容
废话不多说,直接开始 本文以无界作为本文测试案例,抓取shadow-root(open)下的内容 shadow Dom in selenium: 首先先讲一下shadow Dom in selenium 版本的区别,链接指向这里 在Selenium 4版本 以及 chrom…...
AutoClass加载预训练实例
AutoClass 由于 Transformer 架构种类繁多,AtuoClass可以创建一个你想要的做模型架构。作为 🤗 Transformer 核心理念的一部分,使库易于使用、简单且灵活,可以AutoClass从给定的检查点自动推断和加载正确的架构。该from_pretrain…...
在 CentOS 上安装 NFS 服务器
文章目录 1. 在 CentOS 上安装 NFS 服务器1.1 安装 NFS 服务器软件包1.2 配置 NFS 共享目录1.3 配置 NFS 导出文件1.4 启动并启用 NFS 服务1.5 导出共享目录1.6 配置防火墙1.7 检查 NFS 状态 2. 在 CentOS 上安装 NFS 客户端2.1 安装 NFS 客户端软件包2.2 挂载 NFS 共享2.3 配置…...
utf8mb4_unicode_ci、utf8mb4_general_ci、utf8mb4_0900_ai_ci; Mysql 排序字符集的优缺点和选择
标题内容 Mysql的排序字符集真让人头疼,如果两个表的排序字符集不一致,还会导致在进行字段比较的时候直接报错。下面分析几个常用的字符集的优劣和选择。 utf8mb4_unicode_ci 特点 Unicode 标准兼容性高:它是基于 Unicode 标准的排序规则&a…...
星宸SSC8836Q/SSC8836Q-H
SSC8836Q产品是高度集成的多媒体片上系统(SoC)产品,适用于汽车和运动/运动相机等高分辨率智能视频录制和播放应用。 该芯片包括64位双核RISC处理器,先进的图像信号处理器(ISP),高性能的H.265/H。264/MJPEG视频编解码器,智能处理单…...
rk3576 , android14 , 编译, 卡死,android.bp , ninja
问题:我在 编译 android14 的时候, 卡死再 analysing android.bp 这里 ,卡了 3,4 个小时。肯定是有问题的。 如图&…...
3、.Net UI库:MaterialSkin - 开源项目研究文章
MaterialSkin 是一个开源的 WinForms 第三方库,提供了许多仿谷歌设计风格的组件,使得 WinForms 窗体程序更加美观。以下是 MaterialSkin 的一些关键特点和使用方法: 主要特点: 仿谷歌设计风格:MaterialSkin 提供了大量…...
2024年构建PHP应用开发环境
文章目录 前言选择合适的PHP版本安装与配置PHP环境Windows平台Linux平台macOS平台 集成Web服务器数据库连接与管理使用Composer进行依赖管理调试工具的选择代码质量管理部署与持续集成安全性考虑参考资料结语 前言 随着互联网的发展,PHP作为一门成熟的服务器端编程…...
苹果手机iPad投屏到安卓电视,不只有AirPlay一种方法,还可以无线远程投屏!
苹果品牌的设备一般都可以使用airplay功能,将一个屏幕投射到另一个屏幕上。如果是跨品牌或跨系统投屏,airplay就未必能够适应。 提供无线投屏和airplay投屏两种方式的AirDroid Cast已经推出TV版本。苹果手机或iPad可以选择无线(远程ÿ…...
什么是内网什么是外网?区别是什么
内网和外网是计算机网络中的两个基本概念,它们在定义、特点和使用场景上有显著的区别。虎观代理小二将带大家详细了解内网与外网的定义以及它们之间的主要差异,帮助读者更好地理解和应用这两种网络。 内网(局域网,LAN࿰…...
基于Springboot+Vue的在线答题闯关系统
基于SpringbootVue的在线答题闯关系统 前言:随着在线教育的快速发展,传统的教育模式逐渐向互联网教育模式转型。在线答题系统作为其中的一个重要组成部分,能够帮助用户通过互动式的学习方式提升知识掌握度。本文基于Spring Boot和Vue.js框架&…...
html css 图片背景透明
html css图标背景透明 css属性: background-color:transparent; mix-blend-mode: multiply; 完整HTML代码: <html><head><title>Test</title></head><body><div id"test" style"background-col…...
Servlet
一 Servlet Servlet (server applet) 是运行在服务端(tomcat)的Java小程序,是sun公司提供一套定义动态资源规范; 从代码层面上来讲Servlet就是一个接口 用来接收、处理客户端请求、响应给浏览器的动态资源。在整个Web应用中,Servlet主要负责接收处理请求…...
MySQL用法---MySQL Workbench创建数据库和表
1. 连接数据库 打开软件,点击左下角卡片,输入设置的数据库密码,勾选单选框 2. 了解主页面的组成部分 3. 创建数据库 先点击工具栏的创建按钮 再输入数据库名称 点击 Apply 创建 4. 创建数据表 展开数据库,在Tables上右键&…...
WordPress Elementor Page Builde 任意文件读取漏洞复现(CVE-2024-9935)
0x01 产品描述: WordPress Elementor Page Builder 是一款 WordPress 插件,它允许用户以可视化方式创建和编辑网页。0x02 漏洞描述: WordPress 的 Elementor Page Builder 插件的 PDF 生成器插件在 1.7.5 之前的所有版本中都容易受到路径遍历的攻击,包括 1.7.5 rtw_pgaepb_…...
静态链接和动态链接的特点
静态链接 链接方式:在编译时,所有依赖的库代码被直接打包到生成的可执行文件中。这意味着在程序运行时,不需要再加载任何外部库文件。 优点: 独立性强:生成的可执行文件可以在没有依赖库的系统上直接运行&am…...
内核流对象(Kernel Streaming Objects)
内核流对象(Kernel Streaming Objects)是 Windows 系统中用于处理音频、视频等流媒体数据的重要机制。让我详细解释其作用和主要组件: 1. 主要作用: c // 内核流的核心功能 - 音频/视频数据的实时处理 - 多媒体设备驱动开发 - 硬件与软件之间的数据流传…...
Java 在Json对象字符串中查找和提取特定的数据
1、在处理JSON数据时,需要提出个别字段的值,通过正则表达式提取特定的数据 public static void main(String[] args) {//定义多个JSON对象字符串类型,假设每个对象有a,b,c 字段String strJson "{\"a\":1.23,\"b\"…...
21、结构体成员分布
结构体中的成员并不是紧挨着分布的,内存分布遵循字节对齐的原则。 按照成员定义的顺序,遵循字节对齐的原则存储。 字节对齐的原则: 找成员中占据字节数最大的成员,以它为单位进行空间空配 --- 遇到数组看元素的类型 每一个成员距离…...
【深度学习】四大图像分类网络之ResNet
ResNet网络是在2015年由微软实验室中的何凯明等几位提出,在CVPR 2016发表影响深远的网络模型,由何凯明团队提出来,在ImageNet的分类比赛上将网络深度直接提高到了152层,前一年夺冠的VGG只有19层。斩获当年ImageNet竞赛中分类任务第…...
zookeeper学习
解决什么问题? 首先来分析下业务对象,才能对解决的问题进行归纳和总结。它解决的事分布式应用的问题,那么分布式应用会存在哪里问题是由它的业务特性来决定的,这些问题已是为了解决业务的问题。分布式的业务特征有哪些࿱…...
Monkey结合appium模拟操作特定界面
目录 1. 使用 Monkey 操作特定界面(通过UI标识来限制) 2. 结合 uiautomator 或 appium 定位特定元素 步骤: 3. 使用 Monkey Appium 控制特定界面点击 4. 如何结合 Appium 与 Monkey 5. 限制 Monkey 只点击固定界面上的元素 使用 --pc…...
智能指针【C++11】
文章目录 智能指针std::auto_ptr std::unique_ptrstd::shared_ptrstd::shared_ptr的线程安全问题std::weak_ptr 智能指针 std::auto_ptr 管理权转移 auto_ptr是C98中引入的智能指针,auto_ptr通过管理权转移的方式解决智能指针的拷贝问题,保证一个资源…...
plsql 执行存储过程 SYS_REFCURSOR
关键字:plsql 执行存储过程 SYS_REFCURSOR 在PL/SQL中,SYS_REFCURSOR是一种特殊的数据类型,用于表示引用游标,可以用来返回查询结果或者操作数据库中的结果集。 以下是一个使用SYS_REFCURSOR执行存储过程的例子: CR…...
git修改某次commit(白痴版)
第一步 在bash窗口运行 git rebase --interactive commitId^ 比如要改的commitId是 abcedf git rebase --interactive abcedf^键盘 按 i 或者 ins 进入编辑状态 进入insert 编辑状态 在bash窗口手动把对应commit前面的pick改为e或edit 按 esc 进入退出程序 输入 :wq 保存退出…...
设计模式:19、桥接模式
目录 0、定义 1、桥接模式的四种角色 2、桥接模式的UML类图 3、示例代码 0、定义 将抽象部门与实现部分分离,使它们都可以独立地变化。 1、桥接模式的四种角色 抽象(Abstraction):一个抽象类,包含实现者…...
闭包函数的基础知识
上期文章 1. 函数装饰器 2.闭包 2.1变量作用域 python有3种变量作用域 模块全局作用域:在类或函数外部分配定义的。函数局部作用域:通过参数或者在函数主体中定义的。第3种作用域:闭包中的变量环境 2.2全局变量和局部变量 def fun(a):print(a)print(b)fun(10)10---------…...
python3D圣诞树
import pygame import math from pygame.locals import *# 初始化Pygame pygame.init()# 设置屏幕尺寸和标题 width, height 800, 600 screen pygame.display.set_mode((width, height)) pygame.display.set_caption(3D 圣诞树)# 设置颜色 GREEN (34, 139, 34) BROWN (139,…...
博物馆导览系统方案(一)背景需求分析与核心技术实现
维小帮提供多个场所的室内外导航导览方案,如需获取博物馆导览系统解决方案可前往文章最下方获取,如有项目合作及技术交流欢迎私信我们哦~撒花! 一、博物馆导览系统的背景与市场需求 在数字化转型的浪潮中,博物馆作为文化传承和知…...
[代码随想录09]字符串2的总结
前言 处理字符串主要是有思路,同时总结方法。 题目链接 151. 反转字符串中的单词 - 力扣(LeetCode) 55. 右旋字符串(第八期模拟笔试) 一、翻转字符串里的单词 这个题目的主要思路,代码采用从后往前遍历字…...
C语言程序设计P5-3【应用函数进行程序设计 | 第三节】——知识要点:函数的嵌套调用和递归调用
知识要点:函数的嵌套调用和递归调用 视频 目录 一、任务分析 二、必备知识与理论 三、任务实施 一、任务分析 本任务要求用递归法求 n!。 我们知道n!n(n-1)(n-2)……1n(n-1)!递归公式为: 1.上面公式分解为n!n(n-1)!,即将求n!的问题变为…...
【Java】protobuf-maven-plugin主动下载protoc编译proto文件
背景: 我们hadoop的代码里,配置的是3.7.1的protocol buffer,但是编译环境上安装的是2.5.0。 能成功编译,且没有什么兼容性问题。这里非常好奇发生了什么,于是研究了一下protobuf-maven-plugin插件, 发现是他去下载的 在使用protobuf-maven-plugin插件时,我们一般会这么…...
Go学习:变量
目录 1. 变量的命名 2. 变量的声明 3. 变量声明时注意事项 4. 变量的初始化 5. 简单例子 变量主要用来存储数据信息,变量的值可以通过变量名进行访问。 1. 变量的命名 在Go语言中,变量名的命名规则 与其他编程语言一样,都是由字母、数…...
AllegroHand 四指灵巧手:机器人领域的创新力量
Allegro Hand 作为一款高性价比且适应性强的机器人四指灵巧手,凭借其四根手指和十六个独立的电流控制接头,成为机器人复杂抓握、柔性操作以及触觉传感器等研究领域的理想之选。 Allegrohand 四指灵巧手技术特点 机械结构 Allegro Hand的机械结构设计高…...
蓝桥杯准备训练(lesson3 ,c++)
变量与常量 4.1 变量的创建4.2 变量初始化4.3 变量的分类4.4 常量4.4.1 字⾯常量4.4.2 #define定义常量4.4.3 const定义常量4.5 练习练习1:买票练习2:AB问题练习3:鸡兔同笼 4.1 变量的创建 了解清楚了类型,我们使⽤类型做什么呢&…...
Conda + JuiceFS :增强 AI 开发环境共享能力
Conda 是当前 AI 应用开发领域中非常流行的环境和包管理系统,因其能够简单便捷地创建与系统资源相隔离的虚拟环境广受欢迎。 Conda 支持在不同的操作系统上重建相同的工作环境,但在环境共享复用方面仍存在一些挑战。比如,在不同机器上复用相…...
小红薯x-s算法最新补环境教程12-06更新(下)
在上一篇文章中已经讲了如何去定位x-s生成的位置,本篇文章就直接开始撸代码吧 如果没看过的话可以看:小红薯最新x-s算法分析12-06(x-s 56)(上)-CSDN博客 1、获取加密块代码 首先来到参数生成的位置&…...
强化ASPICE合规与认可度:关键策略与实践路径
确保ASPICE(Automotive SPICE)标准的合规性和认可度,是一个涉及多方面努力和持续改进的过程。以下是一些关键步骤和建议,以帮助企业实现这一目标: 一、熟悉ASPICE标准 深入阅读和理解:首先,需要…...
没有合理使用线程池,引发的生产环境BUG
引言 随着多线程和并发处理需求的增加,线程池成为了提升系统性能的重要工具。Java 提供了强大的 ThreadPoolExecutor 类,能够高效地管理线程池,减少线程创建和销毁的开销。然而,当线程池达到其最大容量时,如何优雅地处…...
异步处理与后台任务管理:在 FastAPI 中实现高级特性
异步处理与后台任务管理:在 FastAPI 中实现高级特性 目录 ⚡ 背景任务与异步处理概述🛠️ 使用 BackgroundTasks 执行后台任务🚀 异步视图函数:使用 async 和 await🔄 处理并发任务:提升应用性能与响应能…...
ceph存储池
1、存储池 1、存储池的概念 存储池就是ceph的逻辑分区,专门用来存储对象的 特点 将文件切片成对象,通过hash算法,找到存储池中的pg,池中的pg根据crush算法找到osd节点 存储中的PG数量对性能有重要的影响,过多和过少…...
STM32基于HAL库的串口接收中断触发机制和适用场景
1. HAL_UART_Receive_DMA函数 基本功能 作用:启动一个固定长度的 DMA 数据接收。特点: 需要预先指定接收数据的长度(Size 参数)。DMA 会一直工作直到接收到指定数量的数据,接收完成后触发 HAL_UART_RxCpltCallback 回…...
商务部将对美低价芯片启动调查
今天,商务部新闻发言人就国内有关芯片产业反映自美进口成熟制程芯片低价冲击国内市场事答记者问。有记者问:业界有消息称,中国国内有关成熟制程芯片产业正遭受自美进口产品的不公平竞争,有申请反倾销反补贴调查的诉求,请问商务部是否收到了相关申请?可否向我们介绍有关情…...
Ksliu:特朗普面临四大险恶威胁
近日,美国的右翼媒体《Newsmax》发表了一篇评论文章,题目是《特朗普面临四大险恶威胁》,作者约翰·布朗(John Browne),是英国国会杰出的前议员,还曾担任首相玛格丽特·撒切尔政府的首席顾问。布朗称:去年11月20日特朗普赢得美国大选后,美国及世界…...
诚食讲座预告 | 封小郡:奶农退出与奶业危机——中国奶业如何自毁长城
海报设计:御寒过去几十年来,我国人民的饮食中包括了越来越多的牛奶。与之相随的是奶农群体的一度崛起和最近十年间的大量退出,进口奶冲击本土原料奶市场,本土原料奶越来越由工业化的大牧场生产。2022年来,我国原料奶价格连续三年下滑,如今奶牛养殖亏损面超过80%,整个行业…...
孟彦:8架轰6K现身,传递什么信息?
轰-6K近期,轰-6K轰炸机群的一个不寻常动作,在岛内引起了轩然大波。这款战略轰炸机,在一场演练中,将自己的实力展现得淋漓尽致,也让“台独”武装陷入了焦虑。8架轰-6K出动,飞向台南军港台湾防务部门称,疑似8架轰-6K从西南方向飞入台所谓的“防…...
毛泽东与梁漱溟的延安争论,让梁终生难忘的是毛泽东政治家的风貌和气度
1938年梁漱溟访问延安与毛泽东进行了交流。梁漱溟将自己的《乡村建设理论》推荐给毛泽东,认为中国共产党的理论与实践是错误的。毛泽东对《乡村建设理论》进行了批注并与梁漱溟展开了关于中国社会性质问题的讨论。两人彻夜长谈,却互相没有说服对方。虽然是争论,但是毛泽东却…...
甲流高发,如何应对?专家解读来了
我国流感进入高发期,且99%以上为甲流。1月9日,中国疾控中心发布最新监测数据显示,目前急性呼吸道传染病总体继续呈现上升趋势,流感仍处于季节性流行期,流感病毒阳性率上升趋缓,预计1月中下旬流感活动水平可能会逐步下降。今年的流感病毒有何特点?重点人群的防护该注意什…...