JVM (Java Virtual Machine) JAVA虚拟机. 由堆、栈、方法区所组成,其中栈内存是给线程用的.
每个线程启动后,虚拟机就会为其分配一块栈内存。
每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存.
每个线程程只能有一个活动栈帧,对应着当前正在执行的那个方法.
JAVA7:
程序计数器,java虚拟机栈 为线程私有;
本地方法栈,Java堆,方法区 为线程共享;
java虚拟机栈的单位为栈帧:
一个方法调用另一个方法,会创建很多栈帧吗?
如果一个栈中有动态链接调用别的方法,就会去创建新的栈帧.
栈指向堆是什么意思?
栈中要使用成员变量时,栈中不会存储成员变量,只会存储一个应用地址
递归的调用自己会创建很多栈帧吗?
递归的话也会创建多个栈帧,就是在栈中一直从上往下排下去.
java堆是java虚拟机所管理的内存中最大的一块,是被所有线程共享的一块内存区域,在虚拟机启动时创建。java堆目的就是存放对象实例。
所有的对象实例以及数组都要在堆上分配。
java堆是垃圾收集器管理的主要区域,从内存回收角度来看java堆可分为:新生代和老年代。
从内存分配的角度看,线程共享的Java堆中可能划分出多个线程私有的分配缓冲区。
无论哪个区域,存储的都是对象实例,进一步地划分都是为了 更好地回收内存,或者更快的分配内存。
根据Java虚拟机规范的规定,java堆可以处于物理上不连续的内存空间中。
当前主流的虚拟机都是可扩展的(通过 -Xmx 和 -Xms 控制)。
如果堆中没有内存可以完成实例分配,并且堆也无法再扩展时,将会抛出 OutOfMemoryError 异常。
栈与堆的区别:
对比 | 堆 | 栈 |
---|---|---|
物理地址 | 堆的物理地址分配对对象是不连续的。因此性能慢些。 在GC的时候也要考虑到不连续的分配,所以有各种算法。 | 栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。 |
内存分配 | 堆因为是不连续的,分配的内存是在运行期确认的,因此大小不固定。一般堆远远大于栈。 | 栈是连续的,分配的内存大小要在编译期就确认,大小是固定。 |
存放内容 | 堆存放 对象的实例和数组。更关注的是数据的存储 | 栈存放 局部变量,操作数栈,返回结果。该区更关注的是程序方法的执行。 |
可见度 | 堆对于整个应用程序都是共享、可见的。 | 栈只对于线程是可见的。线程私有。生命周期和线程相同。 |
线程私有的.
运行时常量池:
在Java8开始取代了永久代的一种内存区域。与永久代不同,元空间不使用Java虚拟机堆内存,而是使用本地内存来存储类的元数据信息。
执行引擎,负责执行虚拟机的字节码,一般先进行编译成机器码后执行。
“虚拟机”是一个相对于“物理机”的概念,虚拟机的字节码是不能直接在物理机上运行的,需要 JVM字节码执行引擎编译成机器码后才可在物理机上执行。
直接内存是基于物理内存和Java虚拟机内存的中间内存,能在一些场景中显著提高性能。
直接内存不受Java堆大小限制,它的分配和释放不依赖于JVM的垃圾回收机制,而是通过操作系统提供的本地内存管理函数进行操作。
直接内存是通过操作系统的本地内存管理函数(如malloc()、free()等)来进行分配和释放的,不需要经过JVM的对象分配和垃圾回收机制。
在JDK1.4中引入了NIO(New Input/Output)类,一种基于通道(Chanel)与缓冲区(Buffer)的I/O方式,NIO提供了一套非阻塞式的I/O操作方式,使用直接内存可以提高I/O操作的效率和性能。
可以使用 Native函数库直接分配堆外内存,然后通过一个存储在 Java 中的 DirectByteBuffer 对象作为对这块内存的引用进行操作。
负责自动管理内存的组成部分。帮助Java程序管理内存,对于垃圾对象的清除、存活对象的管理以及内存碎片的回收等工作,都交由GC系统负责。
GC发生在堆中,java语言最显著的特点就是引入了垃圾回收机制,使java程序员在编写程序时 不再考虑内存管理的问题。
程序在运行过程中,会产生大量的内存垃圾.为了确保程序运行时的性能,java虚拟机在程序运行的过程中不断地进行自动的垃圾回收(GC)。
在java中,不需要显式的去释放一个对象的内存的,而是由虚拟机自行执行。
JVM中的垃圾回收线程,是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,
执行时扫描那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
full gc触发时机:
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间。
如果大于则进行 Minor GC,如果小于则看 HandlePromotionFailure 设置是否允许担保失败(不允许则直接Full GC)。
如果允许担保失败,继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,
如果大于则尝试 Minor GC(如果尝试失败也会触发Full GC),如果小于则进行 Full GC。
在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。
新生代 ( Young )被划分为三个区域:Eden、From Survivor、To Survivor。目的是为了使 JVM 能够 更好的管理堆内存中的对象,包括内存的分配以及回收。
新生代中一般保存新出现的对象,所以每次垃圾收集时都发现大批的对象死去,只有少量的对象存活,便采用了 复制算法 ,只需要付出少量存活对象的复制成本就可以完成收集。
老年代中一般保存存活了很久的对象,他们存活率高、没有额外空间对它进行分配担保,就必须采 用 “标记-清理”或者“标记-整理” 算法。
Java8- 永久代就是JVM的方法区。放着一些被虚拟机加载的类信息,静态变量,常量等数据。这个区中的东西比老年代和新生代更不容易回收。
Java8中已经移除了永久代,新加了一个叫做空间的本地内存区.
新生代:堆1/3 老年代:堆2/3 通过参数 –XX:NewRatio=2来指定
eden:新生代8/10 survivor:新生代1/10 通过参数 –XX:SurvivorRatio=8来设定
为什么要这样分代?
其实主要原因就是可以根据各个年代的特点进行对象分区存储,更便于回收,采用最适当的收集算法:
新生代中,每次垃圾收集时都发现大批对象死去,只有少量对象存活,便采用了复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须采用“标记-清理”或者“标记-整理”算法。
新生代又分为Eden和Survivor (From与To)两个区。加上老年代就这三个区。
数据会首先分配到Eden区当中(特殊情况,如果是大对象(大于PretenureSizeThreshold阈值)那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。
当Eden没有足够空间的时候就会触发jvm发起一次Minor GC。
如果对象经过一次Minor-GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空间当中。并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,
当年龄达到一定的程度(默认15)时,就会被晋升到老年代中了,-XX:MaxTenuringThreshold=15,设置晋升年龄.
为什么新生代要分Eden和两个 Survivor 区域?
元空间metaSpace替换永久代perm(方法区移至Metaspace,字符串常量池移至Java Heap)
垃圾收集器是垃圾回收算法(标记清除法、标记整理法、复制算法、分代算法)的具体实现,不同垃圾收集器、不同版本的JVM所提供的垃圾收集器可能会有很在差别。
收集器分为分代收集器和分区收集器:
分代收集器:Serial、ParNew、Parallel Scavenge、CMS、Serial Old、Parallel Old
分区收集器:G1、ZGC(java11)、Shenandoah(java12)
收集器间搭配:
Serial可搭配: Serial Old、CMS
Parallel Scavenge可搭配: Serial Old、Parallel Old
PraNew可搭配: Serial Old、CMS
Serial 收集器(复制算法): 新生代单线程收集器.优点:简单高效. 适合单线程环境和对暂停时间要求不高的应用场景。
ParNew 收集器 (复制算法): 新生代收并行收集器,实际上是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现.用于搭配CMS的新生代收集器.
Parallel Scavenge 收集器 (复制算法): 新生代并行收集器,追求高吞吐量,高效利用CPU。
吞吐量 = 用户线程时间/(用户线程时间+GC线程时间),
高吞吐量可以高效率的利用CPU时间,尽快完成程序的运算任务,适合后台应用等对交互响应 要求不高的场景.
Serial Old 收集器 (标记-整理算法): 老年代单线程收集器,Serial收集器的老年代版本;
Parallel Old 收集器 (标记-整理算法): 老年代并行收集器,吞吐量优先,Parallel Scavenge收集器的老年代版本;
CMS(Concurrent Mark Sweep 并发标记清除回收器)收集器:
老年代并发收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。
以牺牲吞吐量为代价来获得 最短回收停顿时间的垃圾回收器。对于要求服务器响应速度的应用上,非常适合。
在启动 JVM 的参数加上“-XX:+UseConcMarkSweepGC”来指定使用CMS垃圾回收器。
CMS 使用的是标记-清除的算法实现的,所以在gc的时候会产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,
临时CMS会采用 Serial Old 回收器进行垃圾清除(标记整理算法),此时的性能将会被降低。
CMS收集器有单独收集年老代空间的行为.(其他收集器发生老年代GC时,年轻代GC会一起发生)
G1(Garbage First)收集器 (标记-整理算法):
Java堆 并发 分区回收 收集器,G1收集器是JDK1.7提供的一个新收集器,G1收集器基于“标记-整理”算法实现,不会 产生内存碎片。
在不牺牲吞吐量前提下,实现低停顿垃圾回收。
此外,G1收集器回收的范围是整个Java堆(包括新生代,老年代)
JAVA9时变为默认使用的收集器.
堆中的内存区域被划为了一个个Region区。Region区的默认数量限制为2048个.每个区大小为堆空间大小/2048.(不推荐用XX:G1HeapRegionSize指定)
每个分区都可能是年轻代也可能是老年代,但是在同一时刻只能属于某个代。运行时,每个分区都会被打上唯一的分区标识。
JVM不需要再为堆空间分配连续的内存,堆空间可以是不连续物理内存来组成Region的集合.
有的区域垃圾对象少,有的垃圾对象多,G1优先回收垃圾对象多的区域.
-XX:G1NewSizePercent 设置新生代初始占比(默认5)
-XX:G1MaxNewSizePercent 设置新生代最大占比(默认60)
新生代中的Eden区和Survivor区对应的Region区比例默认8:1:1.
G1中的年老代晋升条件和之前的相同,达到年龄阈值的对象会被转入年老代的Region区中.
对于大对象的分配,在G1中不会让大对象进入年老代,在G1中由专门存放大对象的Region区叫做 Humongous 区,
如果在分配对象时,判定出一个对象属于大对象,那么则会直接将其放入Humongous区存储。(超过单个普通Region区的50%为大对象,单个Humongous区存不下时,可能会横跨多个Region区存储)
可以避免一些生命周期短的大对象直接进入年老代,节约年老代的内存空间,可以有效避免年老代因空间不足时的GC开销。
FullGC时,也会对Humongous区进行回收。
YoungGC:
在G1中,当Eden域被用完时,G1首先会计算回收当前的新生代空间需要花费的时间,如果回收时间远远小于参数-XX:MaxGCPauseMills 值(默认200ms),那么不会触发YoungGC,
而是会继续为新生代增加新的Region区用于存放新分配的对象实例。
直至某次Eden区空间再次被放满并经过计算后,此次回收的耗时接近-XX:MaxGCPauseMills参数设定的值,才触发YoungGC。
YoungGC被触发时,首先会将目标Region区中的存活对象移动(多线程并行复制)至幸存区空间(Survivor-from标签的区域).达到晋升年龄标准的对象也会被移入至年老代区中存储.
G1内部做了优化,一旦发现没有引用指向巨型对象,则可直接在年轻代收集周期中被回收。
MixedGC:
当整个堆中年老代的区域占有率达到参数 -XX:InitiatingHeapOccupancyPercent(默认45) 设定的值后触发MixedGC.
触发时会回收所有新生代区和部分年老代区(根据期望的GC停顿时间选择合适的年老代Region区优先回收)以及大对象Humongous区.
FullGC:
当 G1 无法在堆空间中申请新的分区时,G1便会触发担保机制,执行一次STW式单线程的 Full GC,Full GC会对整堆做标记清除和压缩,最后将只包含纯粹的存活对象。
MixedGC 回收过程:
缺点:
优点:
Epsilon(JDK11): 用于测试的无操作收集器,装配该款GC收集器的JVM,在运行期间不会发生任何GC相关的操作,程序所分配的堆空间一旦用完,Java程序就会因OOM原因退出。
ZGC(JDK11):
ZGC主打的是超低延迟与吞吐量,ZGC也会在尽可能堆吞吐量影响不大的前提下,
实现在任意堆内存大小下都可以把垃圾回收的停顿时间限制在10ms以内的低延迟。没有实现分代架构.
ZGC的目的主要有如下四点:
ShenandoahGC(JDK12):追求极致低延迟.没有实现分代架构.
ZGC是基于colored pointers染色指针实现的,而ShenandoahGC是基于brooks pointers转发指针实现。
一般有两种方法来判断:
Java存在着内存泄漏的情况,导致内存泄露的原因:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,
尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收。
public static void gc() {
Runtime.getRuntime().gc();
}
-XX:+ DisableExplicitGC 禁用gc()方法.
ExplicitGCInvokesConcurrent 是G1垃圾回收器的一个JVM参数,用于在执行显式垃圾回收时并发执行部分清理操作。
当设置为true时,当应用程序显式调用System.gc()方法或通过JMX接口执行显式的垃圾回收请求时,
G1垃圾回收器将在执行垃圾回收的同时尽可能地启动并发标记和清理阶段。可以在显式垃圾回收请求期间减少停顿时间。
堆外内存常配合使用System GC
堆外内存主要针对java.nio.DirectByteBuffer,这些对象的创建过程会通过Unsafe接口直接通过os::malloc来分配内存,
然后将内存的起始地址和大小存到java.nio.DirectByteBuffer对象里,这样就可以直接操作这些内存。
这些内存只有在DirectByteBuffer回收掉之后才有机会被回收,因此如果这些对象大部分都移到了old区,但是一直没有触发GC,物理内存可能被他们耗尽.
因此为了避免这种悲剧的发生,通过 -XX:MaxDirectMemorySize 来指定最大的堆外内存大小,
当使用达到了阈值的时候将调用System.gc来做一次full gc,以此来回收掉没有被使用的堆外内存.
显式调用System.gc垃圾回收并不能直接回收堆外内存,而是通过垃圾回收器清理无法访问到的DirectByteBuffer对象,并触发finalize()方法。
在finalize()方法中,可以手动释放堆外内存的资源,通常使用Unsafe接口的freeMemory()方法来释放内存。
在 HotSpot 虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头、实例数据和对齐填充.
用于存储对象自身的运行时数据,如哈希码(hashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等。
占用空间大小根据JVM决定,为JVM的一个字大小,也就是32位JVM中Mark Word占用4个字节,64位JVM中占用8个字节。
默认存储对象的HashCode、分代年龄和锁标志位等信息。
这些信息都是与对象自身定义无关的数据,所以MarkWord被设计成一个非固定的数据结构以便在极小的空间内存存储尽量多的数据。
根据对象的状态复用自己的存储空间,也就是说在运行期间MarkWord里存储的数据会随着’锁标志位’的变化而变化。
32位JVM
64位JVM
Epoch(时间戳):用于记录偏向锁的撤销条件,当其他线程尝试获取该对象的锁时,需要检查该时间戳是否与对象头中的时间戳匹配。
如果不匹配,则偏向锁会被撤销,对象将升级为轻量级锁或重量级锁。
对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。(4字节)
存放类的属性(Field)数据信息,包括父类的属性信息.
数组的实例部分还包括数组的长度.
这部分内存按4字节对齐。
虚拟机要求对象起始地址必须是8字节的整数倍。
填充数据不是必须存在的,仅仅是为了字节对齐这部分内存按8字节补充对齐。
依赖:
<!--堆内存 存储结构-->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
查看对象内存占用信息:
System.out.println(ClassLayout.parseInstance(object).toPrintable());
//省略getter,setter,constructor
public class ObjectHeadTest {
static class LongObject {
private Long num;
}
static class SimpleLongObject {
private long num;
}
static class IntegerObject {
private Integer num;
}
static class IntObject {
private int num;
}
static class LongObjInObj {
private LongObject num;
}
static class SimpleLongObjObjInObj {
private SimpleLongObject num;
}
public static void main(String[] args) {
LongObject longObject = new LongObject(1L);
System.out.println("longObject = " + ClassLayout.parseInstance(longObject).toPrintable());
LongObject nullLongObject = new LongObject();
System.out.println("nullLongObject = " + ClassLayout.parseInstance(nullLongObject).toPrintable());
SimpleLongObject simpleLongObject = new SimpleLongObject(1L);
System.out.println("simpleLongObject = " + ClassLayout.parseInstance(simpleLongObject).toPrintable());
SimpleLongObject nullSimpleLongObject = new SimpleLongObject();
System.out.println("nullSimpleLongObject = " + ClassLayout.parseInstance(nullSimpleLongObject).toPrintable());
IntegerObject integerObject = new IntegerObject(1);
System.out.println("integerObject = " + ClassLayout.parseInstance(integerObject).toPrintable());
IntObject intObject = new IntObject(1);
System.out.println("intObject = " + ClassLayout.parseInstance(intObject).toPrintable());
LongObjInObj longObjInObj = new LongObjInObj(longObject);
System.out.println("longObjInObj = " + ClassLayout.parseInstance(longObjInObj).toPrintable());
SimpleLongObjObjInObj simpleLongInObj = new SimpleLongObjObjInObj(simpleLongObject);
System.out.println("simpleLongInObj = " + ClassLayout.parseInstance(simpleLongInObj).toPrintable());
}
}
程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成可以被虚拟机直接使用的java类型。
Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。
在写程序的时候,几乎不需要关心类的加载,因为这些都是隐式装载的,除非有特殊的用法,像是反射,就需要显式的加载所需要的类。
类装载方式,有两种:
为了节省内存开销,Java类的加载是动态的,并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中.其他类,在需要的时候才加载。
实现通过类的全限定名获取类的二进制字节流的代码块叫做类加载器。
存在多种类加载器:
类加载器顺序:
自定义类加载器的应用场景:
综合运用:比如应用需要通过网络来传输 Java 类的字节码,为了安全性,这些字节码经过了加密处理。
这个时候就需要自定义类加载器来从某个网络地址上读取加密后的字节代码,接着进行解密和验证,最后定义出在Java虚拟机中运行的类。
双亲委派模型的工作过程:
一个类加载器收到了类加载的请求,不会先自己尝试去加载 这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,
这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。
//双亲委派模型的工作过程源码
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
// 检查类是否已存在
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
}
catch (ClassNotFoundException e) {
// 加载失败 抛出ClassNotFoundException thrown if class not found
// from the non-null parent class loader
//父类加载器无法完成类加载请求
}
if (c == null) {
// If still not found, then invoke findClass in order to find the class
//子加载器进行类加载
c = findClass(name);
}
}
if (resolve) {
//判断是否需要链接过程,参数传入
resolveClass(c);
}
return c;
}
好处:
User user = new User();JVM做了哪些操作?
JDK 自带了很多监控工具,都位于 JDK 的 bin 目录下,其中最常用的是jconsole和jvisualvm这两款视图监控工具。
jconsole:用于对JVM中的内存、线程和类等进行监控;
jvisualvm:JDK自带的全能分析工具,可以分析:内存快照、线程快照、程序 死锁、监控内存的变化、gc变化等。
堆配置:
收集器配置:
串行收集器
并行收集器(吞吐量优先)
辅助的GC典型配置参数:
推荐配置
//参数需JDK 8u191+、JDK 10及以上版本。
/使用容器内存。允许JVM从主机读取cgroup限制,例如可用的CPU和RAM,并进行相应的配置。当容器超过内存限制时,会抛出OOM异常,而不是强制关闭容器。
-XX:+UseContainerSupport
//设置JVM使用容器内存的初始百分比。建议与-XX:MaxRAMPercentage保持一致,推荐设置为70.0。
-XX:InitialRAMPercentage=70.0
//设置JVM使用容器内存的最大百分比。由于存在系统组件开销,建议最大不超过75.0,推荐设置为70.0。
-XX:MaxRAMPercentage=70.0
//输出GC详细信息。
-XX:+PrintGCDetails
//输出GC时间戳。日期形式
-XX:+PrintGCDateStamps
//GC日志文件路径。需保证Log文件所在容器路径已存在.
-Xloggc:/home/admin/nas/gc-${
POD_IP}-$(date '+%s').log
//JVM发生OOM时,自动生成DUMP文件。
-XX:+HeapDumpOnOutOfMemoryError
//DUMP文件路径。需保证DUMP文件所在容器路径已存在.
-XX:HeapDumpPath=/home/admin/nas/dump-${
POD_IP}-$(date '+%s').hprof
//设置JVM初始内存大小。建议与-Xmx相同,避免每次垃圾回收完成后JVM重新分配内存。 推荐百分之70左右内存大小.
-Xms2048m
//设置JVM最大可用内存大小。为避免容器OOM,请为系统预留足够的内存大小。
-Xmx2048m
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/home/admin/nas/gc-${
POD_IP}-$(date '+%s').log
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/home/admin/nas/dump-${
POD_IP}-$(date '+%s').hprof
开发工具
线上环境:
真实调优:
使用方式:
行为参数(功能开关):
性能调优:
调试参数:
option参数 | 解释 |
---|---|
-class | 显示ClassLoad的相关信息 |
-compiler | 显示JIT编译的相关信息 |
-gc | 显示和gc相关的堆信息- |
-gccapacity | 显示各个代的容量以及使用情况 |
-gccause | 显示垃圾回收的相关信息(通-gcutil),同时显示最后一次或当前正在发生的垃圾回收的诱因 |
-gcnew | 显示新生代的信息 |
-gcnewcapacity | 显示新生代大小和使用情况 |
-gcold | 显示老年代和永久代的信息 |
-gcoldcapacity | 显示老年代的大小 |
-gcpermcapacity | 显示永久代的大小 |
-gcutil | 显示垃圾收集信息 |
-printcompilation | 输出JIT编译的方法信息 |
参数 | 解释 |
---|---|
-t | 可以在打印的列上加上Timestamp列,用于显示系统运行的时间 |
-h | 可以在周期性数据的时候,可以在指定输出多少行以后输出一次表头 |
interval | 执行每次的间隔时间,单位为毫秒 |
count | 用于指定输出多少次记录,缺省则会一直打印 |
文章浏览阅读1.1k次,点赞2次,收藏17次。效果图wxml代码<!--轮播:远程数据(图片地址+连接地址)【对象数组,数组中的每个元素包含图片地址+链接地址】--><view ><!--swiper属性里面的设置相比上面,多了个if判断是否显示,jieguo=true; jieguo2=false--><!--远程数据地址:http://phpshop.yaoyiwangluo.com/wx_lunbo.php--><swiper indicator-dots="{{indicato_小程序校园购物平台页面
文章浏览阅读3.3k次,点赞7次,收藏30次。1.什么是锁?在单进程的系统中,当存在多个线程可以同时改变某个变量(可变共享变量)时,就需要对变量或代码块做同步,使其在修改这种变量时能够线性执行消除并发修改变量。而同步的本质是通过锁来实现的。为了实现多个线程在一个时刻同一个代码块只能有一个线程可执行,那么需要在某个地方做个标记,这个标记必须每个线程都能看到,当标记不存在时可以设置该标记,其余后续线程发现已经有标记了则等待拥有标记的线程结束同步代码块取消标记后再去尝试设置标记。这个标记可以理解为锁。不同地方实现锁的方式也不一样,只要能满足所有线程都_java 分布式锁
本篇文章介绍了十进制与其他进制之间的相互转换方法,包括十进制转换为二进制、八进制、十六进制,以及其他进制转换为十进制的方法。同时还提供了一些具体的转换示例。
文章浏览阅读4k次,点赞11次,收藏26次。1.概念:数据:Data,是客观事物的符号表示,是所有能输入到计算机中并被计算机程序处理的符号的总称。数据元素:Data Element,是数据的基本单位,在计算机中常作为一个整体进行考虑和处理,用于完整的描述一个对象。数据项:Data Item,是组成数据元素的、有独立含义的、不可分割的最小单位。数据对象:Data Object,是性质相同的数据元素的集合,是数据的一个子集。数据结构:Data Structure,是相互之间存在一种或多种特定关系的数据元素的集合。逻辑结构:从具体问题抽象出来的_简述逻辑结构的四种基本关系并画出它们的关系图
文章浏览阅读887次。///////////////////////////////////////变量语法使用以下语法规则声明 HLSL 变量。[Storage_Class] [Type_Modifier]Type Name[Index] [: Semantic] [: Packoffset] [: Register]; [Annotations] [= Initial_Value]参数存储 _班级可选的存储类修饰符,它们为编译器提示指定变量范围和生存期;可以按任意顺序指定修饰符。值 说明._hlsl compute shader
文章浏览阅读4.3k次,点赞4次,收藏16次。一文彻底搞懂 Alertmanager 的告警抑制与静默。_alertmanager告警
文章浏览阅读7.8k次。SQL在联表查询的时候,如果遇到字表的数据是没有的,如果是普通的查询就会出现一种查不到数据的情况,这时候就需要用到联表查询的使用左连接指向主表(左右连接看功能的具体需求),我的情况是主表连接多个子表(这些表是查询下拉框的数据),因为总有用户会不选上某一个的下拉框的情况,这样就会在修改时绑定数据的时候就会出现数据绑不上的情况我们就可以使用左连接,然后就可以查询到有的数据,就不会受到没有的数..._left outer join 的效果
文章浏览阅读2.9k次,点赞8次,收藏17次。本文是对上一篇文章《逻辑地址、线性地址、物理地址的关系以及段寄存器在不同位数CPU中的用途演变以及GDT LDT PGD PT的关系》的补充。一. 寻址方式:实地址模式和保护地址模式我们知道,内存寻址模式在早期是采用的实地址模式(intel 80286之前),后面发展到了保护模式(80286开始)。在8086的时候,也就是16位cpu的时候,CPU配备了4个16位段寄存器(CS代码段寄存器..._ldtr
文章浏览阅读1.9k次。1、报错场景:在jsp中使用el表达式时,出现JasperException异常。2、报错信息:org.apache.jasper.JasperException: 在 [45] 行处理 [/register.jsp] 时发生异常42: <td style="width:40%">43: <input type="text" clas..._org.apache.jasper.jasperexception: 在 [31] 行处理 [/register1.jsp] 时发生异
文章浏览阅读9.9k次,点赞14次,收藏94次。html零基础01_html5教程
文章浏览阅读2.1w次,点赞5次,收藏6次。1、第一种方式/**function layer_show(title,url,data,w,h){if (title == null || title == ‘’) {title=false;};if (url == null || url == ‘’) {url=“404.html”;};if (w == null || w == ‘’) {w=800;};if (h ..._layer.open data
文章浏览阅读2.1w次,点赞9次,收藏7次。这是来自一位学长的 (业务主管综合面)4.29下午2:10分,全程20min学长的视角:主管也是真的很nice!我每次回答问题后都给我说谢谢,搞得我都不好意思了,整个过程非常随和,完全没有架子,很耐心的给我解释问题,最后退出还说非常感谢面试华为,体验超好,面完五分钟官网刷新通过,十分钟短信通过。总结一下吧,总的来说,我这次华子的面试准备了很多东西,但是基本没问…整个过程体验非常好,不会让你尴尬的,面试官都大赞!给大家分享面筋,希望对还没面试的小伙伴提供参考,不过目前进了池子,得等很久才能出结果,许愿offe_华为实习业务主管面试