JVM 调优和垃圾回收器说明

转载自   JVM 调优和垃圾回收器说明

JVM垃圾收集算法

      JVM垃圾收集算法有四种:标记-清除算法、复制算法、标记-整理算法、分代收集算法

      标记-清除算法:

      该算法如同它的名字一样,分为两个阶段:标记、清除。首先标记出所有需要回收的对象,然后,统一清除这些被标记的对象。该算法的缺点是:1、效率不高;2、产生大量不连续的内存碎片,导致有大量内存剩余的情况下,由于,没有连续的空间来存放较大的对象,从而触发了另一次垃圾收集动作。

      复制算法:

      由于标记-清除算法的效率不高,从而提出了复制算法。复制算法将可用的内存分成两样大小的两块,每次只使用其中一块内存。当这块内存用完之后,就把还存活的对象复制到另外一块上面,然后,把这块清空。复制算法克服了标记-清除算法的两个缺点,但是太浪费内存,相当于内存空间减小了一半。

      随着时间的积累,现在使用的复制算法的虚拟机,不再是把内存分为1:1的两块。因为98%的对象是寿命很短的,创建之后,很快就被回收了,存活下来的只有2%,所以,用来存储存活对象的内存区,可以小一些。现在的商业虚拟机是把可用内存分为一个较大的Eden空间和两个较小的Survivor空间,每次使用Eden和其中的一块Survivor。当回收时,把Eden和Survivor中的存活对象一次复制到另一块Survivor内存区上,然后把Eden和刚才用过的Survivor空间清空。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,这样,每次新产生的对象可以使用90%的内存空间。

      标记-整理算法

      从名字可以看出,该算法是对“标记-清除”算法的改进升级版。同样的该算法分为两个阶段:标记、整理。标记阶段同“标记-清除”算法。整理阶段,不是直接对标记对象进行清理,而是让所有存活的对象都移动到一端,然后,直接把边界以外的内存清空。这就解决了“标记-清除”算法会造成大量不连续内存碎片的问题。

      分代收集算法

      分代收集算法是根据对象的存活周期的不同,将内存划分为几块。当前的商业虚拟机的垃圾收集都采用了该算法。一般把Java堆分成新生代(年轻代)和老年代(年老代)。这样就可以根据各年代中对象的存活周期来选择最合适的收集算法了。新生代,由于只有少量的对象能存活下来,所以选用“复制算法”,只需要付出少量存活对象的复制成本。老年代,由于对象的存活率高,没有额外的空间分担,就必须使用“标记-清除”或“标记-整理”算法。

 

JVM垃圾收集器

      由于内存中的对象,是按存活周期存放在不同的内存块中的,所以,我们选择不同的算法来针对不同的内存块进行垃圾收集。从而,对于,不同的内存块,我们需要有不同的垃圾收集器。

      新生代的垃圾收集器有:Serial收集器、ParNew收集器、Parallel Scavenge收集器

      老年代的垃圾收集器有:Serial Old收集器、Parallel Old收集器、CMS收集器、G1收集器

      

      下面我们来分别介绍一下这些垃圾收集器

      Serial收集器/Serial Old收集器

      Serial收集器/Serial Old收集器,是单线程的,使用“复制”算法。当它工作时,必须暂停其它所有工作线程。特点:简单而高效。对于运行在Client模式下的虚拟机来说是一个很好的选择。

      ParNew收集器

      ParNew收集器,是Serial收集器的多线程版。是运行在Server模式下的虚拟机中首选的新生代收集器。除了Serial收集器外,目前只有它能与CMS收集器配合工作。

      Parallel Scavenge收集器/Parallel Old收集器

      Parallel Scavenge收集器,也是使用“复制”算法的、并行的多线程收集器。这些都和ParNew收集器一样。但它关注的是吞吐量(CPU用于运行用户代码的时间与CPU总消耗时间的比值),而其它收集器(Serial/Serial Old、ParNew、CMS)关注的是垃圾收集时用户线程的停顿时间。

      Parallel Old收集器是Parallel Scavenge收集器的老年代版本。

      CMS收集器

      CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,使用“标记-清除”算法。

      CMS收集器分4个步骤进行垃圾收集工作:

      1、初始标记   2、并发标记   3、重新标记   4、并发清除

      其中“初始标记”、“重新标记”是需要暂停其它所有工作线程的。

      G1收集器

      G1(Garbage First)收集器,基于“标记-整理”算法,可以非常精确地控制停顿。



问题

当我们的代码出现下面的情形时,该算法将无法适应

a)         ObjA.obj = ObjB

b)         ObjB.obj - ObjA

                 这样的代码会产生如下引用情形 objA指向objB,而objB又指向objA,这样当其他所有的引用都消失了之后,objAobjB还有一个相互的引用,也就是说两个对象的引用计数器各为1,而实际上这两个对象都已经没有额外的引用,已经是垃圾了。

               


 

 

2、              根搜索算法

                   根搜索算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点。

 



 

 

目前java中可作为GC Root的对象有

1、    虚拟机栈中引用的对象(本地变量表)

2、    方法区中静态属性引用的对象

3、    方法区中常量引用的对象

4、    本地方法栈中引用的对象(Native对象)

说了这么多,其实我们可以看到,所有的垃圾回收机制都是和引用相关的,那我们来具体的来看一下引用的分类,到底有哪些类型的引用?每种引用都是做什么的呢?

Java中存在四种引用,每种引用如下:

1、  强引用

只要引用存在,垃圾回收器永远不会回收

Object obj = new Object();

//可直接通过obj取得对应的对象 obj.equels(new Object());

而这样 obj对象对后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式。

2、  软引用

非必须引用,内存溢出之前进行回收,可以通过以下代码实现

Object obj = new Object();

SoftReference<Object> sf = new SoftReference<Object>(obj);

obj = null;

sf.get();//有时候会返回null

这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null
软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。

3、  弱引用

第二次垃圾回收时回收,可以通过如下代码实现

Object obj = new Object();

WeakReference<Object> wf = new WeakReference<Object>(obj);

obj = null;

wf.get();//有时候会返回null

wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾

弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null

弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器

4、  虚引用(幽灵/幻影引用)

           垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现

Object obj = new Object();

PhantomReference<Object> pf = new PhantomReference<Object>(obj);

obj=null;

pf.get();//永远返回null

pf.isEnQueued();//返回从内存中已经删除

虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。

虚引用主要用于检测对象是否已经从内存中删除。

在上文中已经提到了,我们的对象在内存中会被划分为5块区域,而每块数据的回收比例是不同的,根据IBM的统计,数据如下图所示:


 

 我们知道,方法区主要存放类与类之间关系的数据,而这部分数据被加载到内存之后,基本上是不会发生变更的,

Java堆中的数据基本上是朝生夕死的,我们用完之后要马上回收的,而Java栈和本地方法栈中的数据,因为有后进先出的原则,当我取下面的数据之前,必须要把栈顶的元素出栈,因此回收率可认为是100%;而程序计数器我们前面也已经提到,主要用户记录线程执行的行号等一些信息,这块区域也是被认为是唯一一块不会内存溢出的区域。在SunHostSpot的虚拟机中,对于程序计数器是不回收的,而方法区的数据因为回收率非常小,而成本又比较高,一般认为是“性价比”非常差的,所以Sun自己的虚拟机HotSpot中是不回收的!但是在现在高性能分布式J2EE的系统中,我们大量用到了反射、动态代理、CGLIBJSPOSGI等,这些类频繁的调用自定义类加载器,都需要动态的加载和卸载了,以保证永久带不会溢出,他们通过自定义的类加载器进行了各项操作,因此在实际的应用开发中,类也是被经常加载和卸载的,方法区也是会被回收的!但是方法区的回收条件非常苛刻,只有同时满足以下三个条件才会被回收!

 

1、所有实例被回收

2、加载该类的ClassLoader被回收

3Class对象无法通过任何途径访问(包括反射)

好了,我们现在切入正题,Java1.2之前主要通过引用计数器来标记是否需要垃圾回收,而1.2之后都使用根搜索算法来收集垃圾,而收集后的垃圾是通过什么算法来回收的呢?

1、    标记-清除算法

2、    复制算法

3、    标记-整理算法

我们来逐一过一下

1、    标记-清除算法


 

 

标记-清除算法采用从根集合进行扫描,对存活的对象对象标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,如上图所示。

标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效,但由于标记-清除算法直接回收不存活的对象,因此会造成内存碎片!

2、    复制算法



 
 

 复制算法采用从根集合扫描,并将存活对象复制到一块新的,没有使用过的空间中,这种算法当控件存活的对象比较少时,极为高效,但是带来的成本是需要一块内存交换空间用于进行对象的移动。也就是我们前面提到的

s0 s1等空间。

 

3、    标记-整理算法


 
 
 

 标记

-整理算法采用标记-清除算法一样的方式进行对象的标记,但在清除时不同,在回收不存活的对象占用的空间后,会将所有的存活对象往左端空闲空间移动,并更新对应的指针。标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。

 

我们知道,JVM为了优化内存的回收,进行了分代回收的方式,对于新生代内存的回收(minor GC)主要采用复制算法,下图展示了minor GC的执行过程。

 



 
 

 对于新生代和旧生代,

JVM可使用很多种垃圾回收器进行垃圾回收,下图展示了不同生代不通垃圾回收器,其中两个回收器之间有连线表示这两个回收器可以同时使用。



 

 而这些垃圾回收器又分为串行回收方式、并行回收方式合并发回收方式执行,分别运用于不同的场景。如下图所示


 

 下面我们来逐一介绍一下每个垃圾回收器。

 

1、    Serial收集器

看名字我们都可以看的出来,这个属于串行收集器。其运行示意图如下


 Serial

收集器是历史最悠久的一个回收器,JDK1.3之前广泛使用这个收集器,目前也是ClientVM ServerVM 44GB以下机器的默认垃圾回收器。串行收集器并不是只能使用一个CPU进行收集,而是当JVM需要进行垃圾回收的时候,需要中断所有的用户线程,知道它回收结束为止,因此又号称“Stop The World 的垃圾回收器。注意,JVM中文名称为java虚拟机,因此它就像一台虚拟的电脑一样在工作,而其中的每一个线程就被认为是JVM的一个处理器,因此大家看到图中的CPU0CPU1实际为用户的线程,而不是真正机器的CPU,大家不要误解哦。

 

串行回收方式适合低端机器,是Client模式下的默认收集器,对CPU和内存的消耗不高,适合用户交互比较少,后台任务较多的系统。

Serial收集器默认新旧生代的回收器搭配为Serial+ SerialOld

2、    ParNew收集器

ParNew收集器其实就是多线程版本的Serial收集器,其运行示意图如下



 同样有

Stop The World的问题,他是多CPU模式下的首选回收器(该回收器在单CPU的环境下回收效率远远低于Serial收集器,所以一定要注意场景哦),也是Server模式下的默认收集器。

 

3、    ParallelScavenge

ParallelScavenge又被称为是吞吐量优先的收集器,器运行示意图如下


 

 ParallelScavenge

所提到的吞吐量=程序运行时间/(JVM执行回收的时间+程序运行时间),假设程序运行了100分钟,JVM的垃圾回收占用1分钟,那么吞吐量就是99%。在当今网络告诉发达的今天,良好的响应速度是提升用户体验的一个重要指标,多核并行云计算的发展要求程序尽可能的使用CPU和内存资源,尽快的计算出最终结果,因此在交互不多的云端,比较适合使用该回收器。

 

4、    ParallelOld

ParallelOld是老生代并行收集器的一种,使用标记整理算法、是老生代吞吐量优先的一个收集器。这个收集器是JDK1.6之后刚引入的一款收集器,我们看之前那个图之间的关联关系可以看到,早期没有ParallelOld之前,吞吐量优先的收集器老生代只能使用串行回收收集器,大大的拖累了吞吐量优先的性能,自从JDK1.6之后,才能真正做到较高效率的吞吐量优先。其运行示意图如下


 
  

 5、 SerialOld

SerialOld是旧生代Client模式下的默认收集器,单线程执行;在JDK1.6之前也是ParallelScvenge回收新生代模式下旧生代的默认收集器,同时也是并发收集器CMS回收失败后的备用收集器。其运行示意图如下


 

 6、 CMS

CMS又称响应时间优先(最短回收停顿)的回收器,使用并发模式回收垃圾,使用标记-清除算法,CMSCPU是非常敏感的,它的回收线程数=CPU+3/4,因此当CPU2核的实惠,回收线程将占用的CPU资源的50%,而当CPU核心数为4时仅占用25%。他的运行示意图如下


 

 CMS

模式主要分为4个过程

 在初始标记的时候,需要中断所有用户线程,在并发标记阶段,用户线程和标记线程

并发执行,而在这个过程中,随着内存引用关系的变化,可能会发生原来标记的对象被释放,进而引发新的垃圾,因此可能会产生一系列的浮动垃圾,不能被回收。

 

CMS 为了确保能够扫描到所有的对象,避免在Initial Marking 中还有未标识到的对象,采用的方法为找到标记了的对象,并将这些对象放入Stack 中,扫描时寻找此对象依赖的对象,如果依赖的对象的地址在其之前,则将此对象进行标记,并同时放入Stack 中,如依赖的对象地址在其之后,则仅标记该对象。

在进行Concurrent Marking minor GC 也可能会同时进行,这个时候很容易造成旧生代对象引用关系改变,CMS 为了应对这样的并发现象,提供了一个Mod Union Table 来进行记录,在这个Mod Union Table中记录每次minor GC 后修改了的Card 的信息。这也是ParallelScavenge不能和CMS一起使用的原因。

CMS产生浮动垃圾的情况请见如下示意图


 

在运行回收过后,c就变成了浮动垃圾。

由于CMS会产生浮动垃圾,当回收过后,浮动垃圾如果产生过多,同时因为使用标记-清除算法会产生碎片,可能会导致回收过后的连续空间仍然不能容纳新生代移动过来或者新创建的大资源,因此会导致CMS回收失败,进而触发另外一次FULL GC,而这时候则采用SerialOld进行二次回收。

同时CMS因为可能产生浮动垃圾,而CMS在执行回收的同时新生代也有可能在进行回收操作,为了保证旧生代能够存放新生代转移过来的数据,CMS在旧生代内存到达全部容量的68%就触发了CMS的回收!

7、    GarbageFirst(G1 )

我们再来看垃圾回收器的总图,刚才我们可以看到,我在图上标记了一个?,其实这是一个新的垃圾回收器,既可以回收新生代也可以回收旧生代,SunHotSpot 1.6u14以上EarlyAccess版本加入了这个回收器,sun公司预期SunHotSpot1.7发布正式版,他是商用高性能垃圾回收器,通过重新划分内存区域,整合优化CMS,同时注重吞吐量和响应时间,但是杯具的是被oracle收购之后这个收集器属于商用收费收集器,因此目前基本上没有人使用,我们在这里也就不多介绍,更多信息可以参考oracle新版本JDK说明。

下面我们再来看下JVM的一些内存分配与回收策略

1、    优先在Edon上分配对象

代码示例

package com.yhj.jvm.gc.edenFirst;/*** @Described:Edon优先划分对象测试* VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc* Edon s0 s1 old*   8  1   1  10* @author YHJ create at 2012-1-3 下午04:44:43* @FileNmae com.yhj.jvm.gc.edenFirst.EdonFirst.java*/public class EdonFirst {private final static int ONE_MB = 1024*1024;/*** @param args* @Author YHJ create at 2012-1-3 下午04:44:38*/public static void main(String[] args) {@SuppressWarnings("unused")byte[] testCase1,testCase2,testCase3,testCase4;testCase1 = new byte[2*ONE_MB];testCase2 = new byte[2*ONE_MB];testCase3 = new byte[2*ONE_MB];//     testCase1 = null;//     testCase2 = null;//     testCase3 = null;testCase4 = new byte[2*ONE_MB];}}

运行结果


 

 结果分析

从运行结果我们可以很清晰的看到,eden8MB的存储控件(通过参数配置),前6MB的数据优先分配到eden区域,当下一个2MB存放时,因空间已满,触发一次GC,但是这部分数据因为没有回收(引用还在,当赋值为null后则不会转移),数据会被复制到s0区域,但是s0区域不够存储,因此直接放入老生代区域,新的2MB数据存放在eden区域

2、    大对象直接进入老生代

代码示例

package com.yhj.jvm.gc.bigObjIntoOld;/*** @Described:大对象直接进入老生代测试* VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc* Edon s0 s1 old*   8  1   1  10* @author YHJ create at 2012-1-3 下午05:28:47* @FileNmae com.yhj.jvm.gc.bigObjIntoOld.BigObjIntoOld.java*/public class BigObjIntoOld {private final static int ONE_MB = 1024*1024;/*** @param args* @Author YHJ create at 2012-1-3 下午04:44:38*/public static void main(String[] args) {@SuppressWarnings("unused")byte[] testCase1,testCase2,testCase3,testCase4;testCase1 = new byte[8*ONE_MB];//     testCase2 = new byte[2*ONE_MB];//     testCase3 = new byte[2*ONE_MB];//     testCase1 = null;//     testCase2 = null;//     testCase3 = null;//     testCase4 = new byte[2*ONE_MB];}}运行结果结果分析我们看到,没有触发GC日志,而数据是直接进入老生代的3、    年长者(长期存活对象)进入老生代代码示例:package com.yhj.jvm.gc.longLifeTimeIntoOld;/*** @Described:当年龄大于一定值的时候进入老生代  默认值15岁* VM params : -Xms20M -Xmx20M -Xmn10M -XX:MaxTenuringThreshold=1-XX:+PrintGCDetails -verbose:gc* Edon s0 s1 old  age*   8  1   1  10   1* @author YHJ create at 2012-1-3 下午05:39:16* @FileNmaecom.yhj.jvm.gc.longLifeTimeIntoOld.LongLifeTimeIntoOld.java*/public class LongLifeTimeIntoOld {private final static int ONE_MB = 1024*1024;/*** @param args* @Author YHJ create at 2012-1-3 下午04:44:38*/public static void main(String[] args) {@SuppressWarnings("unused")byte[] testCase1,testCase2,testCase3,testCase4;testCase1 = new byte[1*ONE_MB/4];testCase2 = new byte[7*ONE_MB+3*ONE_MB/4];testCase2 = null;testCase3 = new byte[7*ONE_MB+3*ONE_MB/4];testCase3 = null;testCase4 = new byte[ONE_MB];}}

运行结果



 结果分析

从代码中我们可以看到,当testCase1划分为0.25MB数据,进行多次大对象创建之后,testCase1应该在GC执行之后被复制到s0区域(s0足以容纳testCase1),但是我们设置了对象的年龄为1,即超过1岁便进入老生代,因此GC执行2次后testCase1直接被复制到了老生代,而默认进入老生代的年龄为15。我们通过profilter的监控工具可以很清楚的看到对象的年龄,如图所示

 


 

 右侧的年代数目就是对象的年龄

 

4、    群体效应(大批中年对象进入老生代)

代码示例

package com.yhj.jvm.gc.dynamicMoreAVG_intoOld;/*** @Described:s0占用空间到达50%直接进入老生代* VM params : -Xms20M -Xmx20M -Xmn10M -XX:MaxTenuringThreshold=15-XX:+PrintGCDetails -verbose:gc* Edon s0 s1 old  age*   8  1   1  10  15*   0.5 0  0   7.5*   7.5 0.5 0  7.5*   7.5  0  0 8* @author YHJ create at 2012-1-3 下午05:50:40* @FileNmae com.yhj.jvm.gc.dynamicMoreAVG_intoOld.MoreAVG_intoOld.java*/public class MoreAVG_intoOld {private final static int ONE_MB = 1024*1024;/*** @param args* @Author YHJ create at 2012-1-3 下午04:44:38*/public static void main(String[] args) {@SuppressWarnings("unused")byte[] testCase1,testCase2,testCase3,testCase4;testCase1 = new byte[7*ONE_MB+ONE_MB/2];testCase2 = new byte[ONE_MB/2];testCase3 = new byte[7*ONE_MB+ONE_MB/2];testCase3 = null;testCase4 = new byte[7*ONE_MB+ONE_MB/2];//     testCase1 = new byte[7*ONE_MB+3*ONE_MB/4];//     testCase2 = new byte[ONE_MB/4];//     testCase3 = new byte[7*ONE_MB+3*ONE_MB/4];}}

运行结果


 

 结果分析

我们看到,当创建后testCase3testCase2被移动到s0区域,当被释放后,继续创建testCase3,按理说testCase2应该移动到s1区域,但是因为超过了s1区域的1/2,因此直接进入老生代

5、    担保GC(担保minorGC)

担保GC就是担保minorGC能够满足当前的存储空间,而无需触发老生代的回收,由于大部分对象都是朝生夕死的,因此,在实际开发中这种很起效,但是也有可能会发生担保失败的情况,当担保失败的时候会触发FullGC,但是失败毕竟是少数,因此这种一般是很划算的。

 

代码示例

package com.yhj.jvm.gc.securedTransactions;/*** @Described:担保交易测试* VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc-XX:-HandlePromotionFailure  无担保* VM params : -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -verbose:gc-XX:+HandlePromotionFailure  有担保* Edon s0 s1 old  *   8  1   1  10  * @author YHJ create at 2012-1-3 下午06:11:17* @FileNmaecom.yhj.jvm.gc.securedTransactions.SecuredTransactions.java*/public class SecuredTransactions {private final static int ONE_MB = 1024*1024;/*** @param args* @Author YHJ create at 2012-1-3 下午04:44:38*/public static void main(String[] args) {@SuppressWarnings("unused")byte[] testCase1,testCase2,testCase3,testCase4,testCase5,testCase6,testCase7;testCase1 = new byte[2*ONE_MB];testCase2 = new byte[2*ONE_MB];testCase3 = new byte[2*ONE_MB];testCase1 = null;testCase4 = new byte[2*ONE_MB];testCase5 = new byte[2*ONE_MB];testCase6 = new byte[2*ONE_MB];testCase4 = null;testCase5 = null;testCase6 = null;testCase7 = new byte[2*ONE_MB];}}

 运行结果

1、  无担保


    
 2、
有担保

 结果分析

我们可以很清楚的看到,当无担保的时候,触发了一次FullGC 而有担保的情况下,只有monorGC则完成了回收,大大提升了效率。

当我们注释掉对应的代码

//     testCase4 = null;
//     testCase5 = null;
//     testCase6 = null;

 的时候,就会引发担保失败,如下图所示

JVM默认情况是是开启担保的,无需设置参数。


 2、

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python中的数组按顺序切片_python切片(获取一个子列表(数组))详解

切片&#xff1a;切片指从现有列表中&#xff0c;获取一个子列表返回一个新列表&#xff0c;不影响原列表。下标以 0 开始&#xff1a;list [红,绿,蓝,白,黑,黄,青]# 下标 0 1 2 3 4 5 6取单个值语法&#xff1a;列表[n]n为下标&#xff0c;n0表示第一个 &#xff0c; n1表示第…

.net core快速上手

2014年11月12日的Connect ();开发者活动上宣布将.NET堆栈基于MIT协议开源&#xff0c;并且提供开源保证&#xff0c;托管在Github上。当时的版本与最终目标相距甚远&#xff0c;然而有一点可以肯定的是&#xff0c;这是一个与.NET Framework 4.x完全不同的框架。 这在社区引发了…

hibernate正向生成数据库表以及配置——Teacher.java

package cn.bdqn.studentInfo.entity;import java.util.HashSet; import java.util.Set;/*** 老师的实体类* author Administrator**/ public class Teacher {private Integer id;private String name;private Set<Student> students new HashSet<Student>();publi…

19年8月 字母哥 第五章 静态资源与模板引擎的整合 用热点公司网不行

第五章 静态资源与模板引擎的整合 5.1.webjars与静态资源 5.2.模板引擎选型与未来趋势 5.3.web应用开发之整合jsp 5.4.web应用开发之整合freemarker 5.5.web应用开发之整合thymeleaf 5.6.thymeleaf基础语法讲解 5.7.thymeleaf内置对象与工具类 5.8.公共片段(标签)与内联…

python 随机排序_Python 如何随机打乱列表(List)排序

场景&#xff1a;现在有一个list:[1,2,3,4,5,6]&#xff0c;我需要把这个list在输出的时候&#xff0c;是以一种随机打乱的形式输出。专业点的术语&#xff1a;将一个容器中的数据每次随机逐个遍历一遍。注意&#xff1a;不是生成一个随机的list集。环境&#xff1a;Python 3.6…

深入JVM虚拟机(四) Java GC收集器

转载自 深入JVM虚拟机(四) Java GC收集器1 GC收集器 1.1 Serial串行收集器 串行收集器主要有两个特点&#xff1a;第一&#xff0c;它仅仅使用单线程进行垃圾回收&#xff1b;第二&#xff0c;它独占式的垃圾回收。 在串行收集器进行垃圾回收时&#xff0c;Java 应用程序中的线…

JavaScript(笔记)

UI框架 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yYMMLanm-1608952213820)(C:\Users\王东梁\AppData\Roaming\Typora\typora-user-images\image-20201225142522925.png)] 简介 javaScript是一门世界上最流行的脚本语言 Java&#xff0c;Java…

19年8月 字母哥 第六章 生命周期内的拦截过滤与监听 用热点公司网不行

第六章 生命周期内的拦截过滤与监听 6.1.servlet与filter与listener的实现 servlet的filter(过滤器)listener(监听器) 6.2.spring拦截器及请求链路说明 6.3.自定义事件的发布与监听 6.4.应用启动的监听 第六章 生命周期内的拦截过滤与监听 6.1.servlet与filter与listener的…

外链式样式表_引入CSS样式表(书写位置)

CSS初识CSS(Cascading Style Sheets) 美化样式CSS通常称为CSS样式表或层叠样式表(级联样式表)&#xff0c;主要用于设置HTML页面中的文本内容(字体、大小、对齐方式等)、图片的外形(宽高、边框样式、边距等)以及版面的布局等外观显示样式。CSS以HTML为基础&#xff0c;提供了丰…

JVM 内存分代、垃圾回收漫谈

转载自 JVM 内存分代、垃圾回收漫谈这篇主要简单分享一下关于 JVM 内存模型、内存溢出、内存分代、以及垃圾回收算法的相关知识。如果这篇文章让你对相关知识产生了兴趣而意犹未尽&#xff0c;推荐去阅读周志明老师的《深入理解Java虚拟机》。 JVM 内存区域 这里也简单罗列一下…

hibernate正向生成数据库表以及配置——Student.hbm.xml

<?xml version"1.0" encoding"utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Mapping file au…

ASP.NET Core 之 Identity 入门(二)

前言 在 上篇文章 中讲了关于 Identity 需要了解的单词以及相对应的几个知识点&#xff0c;并且知道了Identity处在整个登入流程中的位置&#xff0c;本篇主要是在 .NET 整个认证系统中比较重要的一个环节&#xff0c;就是 认证&#xff08;Authentication&#xff09;&#xf…

MySQL笔记汇总---狂神说

公众号笔记 1初识MySQL 2数据库操作 3DML语言 4使用DQL查询数据 5MySQL函数 6事务和索引 7权限及如何设计数据库

软件记录

Tampermonkey 加一个脚本可以下载百度文档的文章 不过还是冰点好用 直接下载下来百度文档&#xff0c; 树洞ocr 2.3兆 可以一段一段的进行图片识别 天若OCR开源版V5.0.0 processon 百度搜索 类似于 viso

python快捷方式图标_python – PyInstaller无法更改快捷方式图标

我的问题如下,虽然我可以在可执行文件本身上设置我喜欢的任何图标,但我无法更改此图标我尝试了所有的东西,但是当我选择exe文件或者当我创建一个快捷方式时,这个PyInstaller图标就会出现&#xff01;这是exe看起来的样子这是棘手的部分,如果我设置选项–onefile,这不会发生.如果…

Typora的使用技巧

不能更好的对齐&#xff0c;按enter键不能删除前缀 Ctrl加/&#xff1a;查看源代码 删除多余的-即可 嘿嘿

Java中几种常量池的区分

转载自 Java中几种常量池的区分在java的内存分配中&#xff0c;经常听到很多关于常量池的描述&#xff0c;我开始看的时候也是看的很模糊&#xff0c;网上五花八门的说法简直太多了&#xff0c;最后查阅各种资料&#xff0c;终于算是差不多理清了&#xff0c;很多网上说法都有…

perform指标分析_performace 监控统计

浏览器浏览器是多进程的&#xff0c;js是单线程的。进程&#xff1a;程序运行&#xff0c;资源分配。线程&#xff1a;进程中独立执行的流浏览器有哪些进程Browser进程&#xff1a; 浏览器的主进程(负责协调、主控)GPU进程&#xff1a; 最多一个&#xff0c;用于3D绘制等浏览器…

hibernate正向生成数据库表以及配置——Teacher.hbm.xml

<?xml version"1.0" encoding"utf-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <!-- Mapping file au…

数据库中modify和change的区别

修改字段 : ALTER TABLE 表名 MODIFY 字段名 列类型[属性] 无法重命名ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[属性] 可以重命名