首页 文章 精选 留言 我的

精选列表

搜索[图像理解],共10000篇文章
优秀的个人博客,低调大师

jvm原理解析——不疯魔不成活

摘要: 作为一名java开发人员,如果有人问你java是什么?java是如何运行的?你该如何回答,事实上java是有Java语言、class文件、jvm、Java API共同组成。 java程序运行 *.java文件-->编译器-->*.class文件-->线程启动(main)-->jvm-->操作系统-->硬件 通过上面的流程我们可以看出java程序的执行顺序,那么jvm到底是什么,class文件到底是如何在jvm中运行就显得很重要了。 jvm原理 什么是jvm openjdk源码地址http://hg.openjdk.java.net/jdk9 JVM是一个计算机模型,JVM对Java可执行代码,即字节码(Bytecode)的格式给出了明确的规格。这一规格包括操作码和操作数的语法(也就是cpu指令集)和数值、标识符的数值表示方式、以及Java类文件中的Java对象、常量缓冲池在JVM的存储映象。 JVM的组成 JVM指令系统、JVM寄存器、JVM 栈结构、JVM 碎片回收堆、JVM 存储区 JVM指令 Java指令也是由操作码和操作数两部分组成,与RISC CPU采用的编码方式是一致的,也就是精简指令集,目前UNIX、Linux、MacOS系统使用RISC,我们目前知道的x86架构CPU使用的CISC编码,也就是复杂指令集。 JVM寄存器 1.pc程序计数器 2.optop操作数栈顶指针 3.frame当前执行环境指针 4.vars指向当前执行环境中第一个局部变量的指针 jvm的装载 windows操作系统装入JVM是通过jdk中Java.exe来完成,通过下面4步来完成JVM环境。 1.创建JVM装载环境和配置 2.装载JVM.dll(C:Program FilesJavajre1.8.0_151inserver linux在jre/lib/server下) 3.初始化JVM.dll并挂接到JNIENV(JNI调用接口)实例 4.调用JNIEnv实例装载并处理class类。 JVM虚拟机相当于x86计算机系统,Java解释器相当于x86CPU JVM运行数据 JVM定义了若干个程序执行期间使用的数据区域。这个区域里的一些数据在JVM启动的时候创建,在JVM退出的时候销毁。而其他的数据依赖于每一个线程,在线程创建时创建,在线程退出时销毁。分别有程序计数器,堆,栈,方法区,运行时常量池 程序计数器:每个线程一旦被创建就拥有了自己的程序计数器。当线程执行Java方法的时候,它包含该线程正在被执行的指令的地址。但是若线程执行的是一个本地的方法,那么程序计数器的值就不会被定义。 常量缓冲池和方法区:常量缓冲池用于存储类名称、方法和字段名称以及串常量。方法区则用于存储Java方法的字节码。对于这两种存储区域具体实现方式在JVM规格中没有明确规定。这使得Java应用程序的存储布局必须在运行过程中确定,依赖于具体平台的实现方式。 栈:Java栈是JVM存储信息的主要方法。当JVM得到一个Java字节码应用程序后,便为该代码中一个类的每一个方法创建一个栈框架,以保存该方法的状态信息。每个栈框架包括以下三类信息: 局部变量:对应vars寄存器指向该变量表中的第一个局部变量,用于存储一个类的方法中所用到的局部变量。 执行环境:对应frame寄存器的当前执行环境指针,用于保存解释器对Java字节码进行解释过程中所需的信息。它们是:上次调用的方法、局部变量指针和操作数栈的栈顶和栈底指针。执行环境是一个执行一个方法的控制中心。例如:如果解释器要执行iadd(整数加法),首先要从frame寄存器中找到当前执行环境,而后便从执行环境中找到操作数栈,从栈顶弹出两个整数进行加法运算,最后将结果压入栈顶。 操作数栈:对应optop寄存器的操作数栈顶指针,操作数栈用于存储运算所需操作数及运算的结果。 堆:JVM中最大的,应用的对象和数据都是存在这个区域,这块区域也是线程共享的,也是 gc 主要的回收区,一个 JVM 实例只存在一个堆类存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,以方便执行器执行。 垃圾回收机制(GC)只发生在线程共享区,也就是堆和方法区,栈不需要回收,线程销毁则栈也销毁, 也就是上图的heap space与method area会发生gc。 通过上图可以发现heap space被分为两部分: Young Generation:又分为Eden space所有的类都是在Eden space被new出来的。From区(Survivor 0 space)和To区(Survivor 1 space)。当Eden space空间用完时,程序又需要创建对象,JVM的垃圾回收器将对Eden space进行垃圾回收(Minor GC),将Eden space中的剩余对象移动到From区。若From区也满了,再对该区进行垃圾回收,然后移动到To区。那如果To区也满了呢,再移动到Old区。 Old Generation:若该区也满了,那么这个时候将产生Major GC(FullGCC),进行Tenured区的内存清理。若该区执行Full GC 之后发现依然无法进行对象的保存,产生异常java.lang.OutOfMemoryError: Java heap space。 Java虚拟机的堆内存设置不够,可以通过参数-Xms、-Xmx来调整。 代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)。 Permanent Generation:是一个常驻内存区域,用于存放JDK自身所携带的 Class,Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。产生异常java.lang.OutOfMemoryError: PermGen space jdk1.8之后已经不会再报报这个错误了。因为类信息的卸载几乎很少发生,这样会影响GC的效率。于是PermGen便被拆分出去了。 程序启动需要加载大量的第三方jar包。例如:在一个Tomcat下部署了太多的应用。 大量动态反射生成的类不断被加载,最终导致Perm区被占满。 jvm的算法 由于算法篇幅太长具体算法可自行查阅资料,主要介绍gc算法发生在什么区。 分代搜集算法:是由复制算法、标记/整理、标记/清除算法共同组成 复制算法发生在Young Generation 标记/整理和标记/清除算法发生在Old Generation和Permanent Generation java验证jvm 栈中一般存放的都是对象的指针和基本类型,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。 栈数据可以共享 /*** Created by liustc on 2018/4/20.*/public class JvmTest { public static void main(String[] args) { int a=0; int b=0; System.out.print(a==b); }} "C:Program FilesJavajdk1.8.0_151injava"trueProcess finished with exit code 0 编译器先处理int a = 0;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为0的地址,没找到,就开辟一个存放0这个字面值的地址,然后将a指向0的地址。接着处理int b = 0;在创建完b的引用变量后,由于在栈中已经有0这个字面值,便将b直接指向0的地址。这样,就出现了a与b同时均指向0的情况 /*** Created by liustc on 2018/4/20.*/public class JvmTest { public static void main(String[] args) { int a=0; int b=0; a=1; System.out.print("a="+a); System.out.print("b="+b); System.out.print(a==b); }} "C:Program FilesJavajdk1.8.0_151injava" a=1b=0falseProcess finished with exit code 0 再令a=1;那么,b不会等于1,还是等于0。在编译器内部,遇到a=1;时,它就会重新搜索栈中是否有1的字面值,如果没有,重新开辟地址存放1的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。 String str = "abc"的工作原理 /*** Created by liustc on 2018/4/20.*/public class JvmTest { public static void main(String[] args) { String str1 = "abc"; String str2 = "abc"; System.out.println(str1==str2); }} "C:Program FilesJavajdk1.8.0_151injava"trueProcess finished with exit code 0 /*** Created by liustc on 2018/4/20.*/public class JvmTest { public static void main(String[] args) { String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; System.out.println(str1 + "," + str2); System.out.println(str1==str2); }} "C:Program FilesJavajdk1.8.0_151injava" bcd,abcfalseProcess finished with exit code 0 赋值的变化导致了类对象引用的变化,str1指向了另外一个新对象!而str2仍旧指向原来的对象。上例中,当我们将str1的值改为"bcd"时,JVM发现在栈中没有存放该值的地址,便开辟了这个地址,并创建了一个新的对象,其字符串的值指向这个地址。 /*** Created by liustc on 2018/4/20.*/public class JvmTest { public static void main(String[] args) { String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; String str3 = str1; System.out.println(str3); String str4 = "bcd"; System.out.println(str1 == str4); }} "C:Program FilesJavajdk1.8.0_151injava"bcdtrueProcess finished with exit code 0 str3这个对象的引用直接指向str1所指向的对象(注意,str3并没有创建新对象)。当str1改完其值后,再创建一个String的引用 str4,并指向因str1修改值而创建的新的对象。可以发现,这回str4也没有创建新的对象,从而再次实现栈中数据的共享。 堆验证 String类 /*** Created by liustc on 2018/4/20.*/public class JvmTest { public static void main(String[] args) { String str1 = new String("abc"); String str2 = "abc";System.out.println(str1==str2);}} "C:Program FilesJavajdk1.8.0_151injava"falseProcess finished with exit code 0 以上代码说明,只要是用new()来新建对象的,都会在堆中创建,而且其字符串是单独存值的,即使与栈中的数据相同,也不会与栈中的数据共享。 使用String str = "abc";的方式,可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。这个思想应该是 享元模式的思想。 由于String类的性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。 执行时间上寄存器 < 堆栈 < 堆 /*** Created by liustc on 2018/4/20.*/public class JvmTest { public static void main(String[] args) { String s1 = "ja"; String s2 = "va"; String s3 = "java"; String s4 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3.equals(s4)); }} "C:Program FilesJavajdk1.8.0_151injava"falsetrueProcess finished with exit code 0 是不是很矛盾啊!是不是又懵逼了? 打印false的原因是,java 重载了“+”,查看java字节码可以发现“+”其实是调用了StringBuilder 所以使用了“+”其实是生成了一个新的对象。所以(s3 == s4)打印false /*** Created by liustc on 2018/4/20.*/public class JvmTest { public static void main(String[] args){ long maxMemory = Runtime.getRuntime().maxMemory();//返回Java虚拟机试图使用的最大内存量。 Long totalMemory = Runtime. getRuntime().totalMemory();//返回jvm实例占用的内存。 System.out.println("MAX_MEMORY ="+maxMemory +"(字节)、"+(maxMemory/(double)1024/1024) + "MB"); System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字节)"+(totalMemory/(double)1024/1024) + "MB"); }} "C:Program FilesJavajdk1.8.0_151injava" -XX:+PrintGCDetailsMAX_MEMORY =1868038144(字节)、1781.5MBTOTAL_ MEMORY = 126877696(字节)121.0MBHeapPSYoungGen total 37888K, used 3932K [0x00000000d6400000, 0x00000000d8e00000, 0x0000000100000000) eden space 32768K, 12% used [0x00000000d6400000,0x00000000d67d7320,0x00000000d8400000) from space 5120K, 0% used [0x00000000d8900000,0x00000000d8900000,0x00000000d8e00000) to space 5120K, 0% used [0x00000000d8400000,0x00000000d8400000,0x00000000d8900000)ParOldGen total 86016K, used 0K [0x0000000082c00000, 0x0000000088000000, 0x00000000d6400000) object space 86016K, 0% used [0x0000000082c00000,0x0000000082c00000,0x0000000088000000)Metaspace used 3325K, capacity 4494K, committed 4864K, reserved 1056768K class space used 363K, capacity 386K, committed 512K, reserved 1048576KProcess finished with exit code 0 将jvm堆初始值改小,触发gc回收 import java.util.Random;/*** Created by liustc on 2018/4/20.*/public class JvmTest { public static void main(String[] args){ long maxMemory = Runtime.getRuntime().maxMemory();//返回jvm试图使用的最大内存量。 Long totalMemory = Runtime. getRuntime().totalMemory();//返回jvm实例的内存大小。 System.out.println("MAX_MEMORY ="+maxMemory +"(字节)、"+(maxMemory/(double)1024/1024) + "MB"); System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字节)"+(totalMemory/(double)1024/1024) + "MB"); String str = "www.baidu.com"; while(true){ str += str + new Random().nextInt(88888888) + new Random().nextInt(99999999); } }} "C:Program FilesJavajdk1.8.0_151injava" -XX:+PrintGCDetailsMAX_MEMORY =1868038144(字节)、1781.5MBTOTAL_ MEMORY = 126877696(字节)121.0MB[GC (Allocation Failure) [PSYoungGen: 32247K->2729K(37888K)] 32247K->10124K(123904K), 0.0045031 secs] [Times: user=0.01 sys=0.03, real=0.00 secs][GC (Allocation Failure) [PSYoungGen: 32912K->4469K(70656K)] 40307K->26638K(156672K), 0.0121112 secs] [Times: user=0.00 sys=0.00, real=0.01 secs][GC (Allocation Failure) [PSYoungGen: 66160K->776K(70656K)] 88329K->59879K(156672K), 0.0141096 secs] [Times: user=0.03 sys=0.02, real=0.01 secs][Full GC (Ergonomics) [PSYoungGen: 776K->0K(70656K)] [ParOldGen: 59103K->37630K(116224K)] 59879K->37630K(186880K), [Metaspace: 3408K->3408K(1056768K)], 0.0143902 secs] [Times: user=0.03 sys=0.00, real=0.01 secs][Full GC (Ergonomics) [PSYoungGen: 60370K->0K(70656K)] [ParOldGen: 96726K->74565K(172032K)] 157096K->74565K(242688K), [Metaspace: 3409K->3409K(1056768K)], 0.0598124 secs] [Times: user=0.08 sys=0.00, real=0.06 secs][GC (Allocation Failure) [PSYoungGen: 60382K->32K(95744K)] 1257771K->1226968K(1463808K), 0.0227293 secs] [Times: user=0.06 sys=0.01, real=0.02 secs][GC (Allocation Failure) [PSYoungGen: 32K->32K(131584K)] 1226968K->1226968K(1499648K), 0.0037586 secs] [Times: user=0.00 sys=0.00, real=0.00 secs][Full GC (Allocation Failure) [PSYoungGen: 32K->0K(131584K)] [ParOldGen: 1226936K->355271K(483840K)] 1226968K->355271K(615424K), [Metaspace: 3409K->3409K(1056768K)], 0.1616835 secs] [Times: user=0.19 sys=0.09, real=0.16 secs][GC (Allocation Failure) [PSYoungGen: 2499K->32K(158208K)] 1303306K->1300838K(1526272K), 0.0037952 secs] [Times: user=0.06 sys=0.00, real=0.00 secs][GC (Allocation Failure) [PSYoungGen: 32K->32K(158208K)] 1300838K->1300838K(1526272K), 0.0036491 secs] [Times: user=0.00 sys=0.00, real=0.00 secs][Full GC (Allocation Failure) [PSYoungGen: 32K->0K(158208K)] [ParOldGen: 1300806K->473463K(622080K)] 1300838K->473463K(780288K), [Metaspace: 3409K->3409K(1056768K)], 0.1641897 secs] [Times: user=0.30 sys=0.06, real=0.16 secs][GC (Allocation Failure) [PSYoungGen: 0K->0K(250880K)] 946230K->946230K(1618944K), 0.0027229 secs] [Times: user=0.00 sys=0.00, real=0.00 secs][GC (Allocation Failure) [PSYoungGen: 0K->0K(258560K)] 946230K->946230K(1626624K), 0.0027747 secs] [Times: user=0.00 sys=0.00, real=0.00 secs][Full GC (Allocation Failure) [PSYoungGen: 0K->0K(258560K)] [ParOldGen: 946230K->709846K(879104K)] 946230K->709846K(1137664K), [Metaspace: 3409K->3409K(1056768K)], 0.1013768 secs] [Times: user=0.28 sys=0.02, real=0.10 secs][GC (Allocation Failure) [PSYoungGen: 0K->0K(353280K)] 709846K->709846K(1721344K), 0.0049384 secs] [Times: user=0.00 sys=0.00, real=0.01 secs][Full GC (Allocation Failure) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space[PSYoungGen: 0K->0K(353280K)] [ParOldGen: 709846K->709816K(900608K)] 709846K->709816K(1253888K), [Metaspace: 3409K->3409K(1056768K)], 0.1792920 secs] [Times: user=0.39 sys=0.00, real=0.18 secs]Heapat java.util.Arrays.copyOf(Arrays.java:3332) PSYoungGen total 353280K, used 14028K [0x00000000d6400000, 0x00000000ec700000, 0x0000000100000000)at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) eden space 352768K, 3% used [0x00000000d6400000,0x00000000d71b3070,0x00000000ebc80000)at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674) from space 512K, 0% used [0x00000000ec680000,0x00000000ec680000,0x00000000ec700000)at java.lang.StringBuilder.append(StringBuilder.java:208) to space 4608K, 0% used [0x00000000ebe00000,0x00000000ebe00000,0x00000000ec280000)at JvmTest.main(JvmTest.java:15) ParOldGen total 1368064K, used 709816K [0x0000000082c00000, 0x00000000d6400000, 0x00000000d6400000)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) object space 1368064K, 51% used [0x0000000082c00000,0x00000000ae12e1e8,0x00000000d6400000) Metaspace used 3440K, capacity 4494K, committed 4864K, reserved 1056768Kat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) class space used 377K, capacity 386K, committed 512K, reserved 1048576Kat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)Process finished with exit code 1 欢迎工作一到五年的Java程序员朋友们加入Java架构开发:744677563 本群提供免费的学习指导 架构资料 以及免费的解答 不懂得问题都可以在本群提出来 之后还会有职业生涯规划以及面试指导

优秀的个人博客,低调大师

Spring Boot轻松理解动态注入,删除bean

原文地址:http://412887952-qq-com.iteye.com/blog/2348445 ​ 我们通过getBean来获得对象,但这些对象都是事先定义好的,我们有时候要在程序中动态的加入对象.因为如果采用配置文件或者注解,我们要加入对象的话,还要重启服务,如果我们想要避免这一情况就得采用动态处理bean,包括:动态注入,动态删除。 本节大纲 : (1)动态注入bean思路; (2)动态注入实现代码; (3)多次注入同一个bean的情况; (4)动态删除; ​ 接下来我们看下具体的内容: (1)动态注入bean思路; ​ 在具体进行代码实现的时候,我们要知道,Spring管理bean的对象是BeanFactory,具体的是DefaultListableBeanFactory,在这个类当中有一个注入bean的方法:registerBeanDefinition,在调用registerBeanDefinition方法时,需要BeanDefinition参数,那么这个参数怎么获取呢?Spring提供了BeanDefinitionBuilder可以构建一个BeanDefinition,那么我们的问题就是如何获取BeanFactory了,这个就很简单了,只要获取到ApplicationContext对象即可获取到BeanFacory了。 (2)动态注入实现代码; 综上所述,如果我们要编写一个简单里的例子的话,那么分以个几个步骤进行编码即可进行动态注入了: <1>. 获取ApplicationContext; <2>. 通过ApplicationContext获取到BeanFacotory; <3>. 通过BeanDefinitionBuilder构建BeanDefiniton; <4>. 调用beanFactory的registerBeanDefinition注入beanDefinition; <5>. 使用ApplicationContext.getBean获取bean进行测试; ​很明显我们需要先定义个类进行测试,比如TestService代码如下: Java代码 package com.kfit.demo.service; public class TestService { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public void print(){ System.out.println("动态载入bean,name="+name); } } 注意:这里没有使用@Service和配置文件进行注入TestService。 那么下面我们的目标就是动态注入TestService了,根据以上的分析,我们进行编码,具体代码如下: Java代码 //获取context. -- Angel -守护天使 ApplicationContext ctx = (ApplicationContext) SpringApplication.run(App.class, args); //获取BeanFactory DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory(); //创建bean信息. BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class); beanDefinitionBuilder.addPropertyValue("name","张三"); //动态注册bean. defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinitionBuilder.getBeanDefinition()); //获取动态注册的bean. TestService testService =ctx.getBean(TestService.class);、testService.print(); 执行代码我们会在控制台看到如下打印信息: 动态载入bean,name=张三 ​ 到这里,就证明我们的代码很成功了。 (3)多次注入同一个bean的情况; ​ 多次注入同一个bean的,如果beanName不一样的话,那么会产生两个Bean;如果beanName一样的话,后面注入的会覆盖前面的。 第一种情况:beanName一样的代码: Java代码 beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class); defaultListableBeanFactory.registerBeanDefinition("testService", beanDefinitionBuilder.getBeanDefinition()); 运行看控制台: 动态载入bean,name=李四 第二种情况:beanName不一样的代码: Java代码 beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(TestService.class); defaultListableBeanFactory.registerBeanDefinition("testService1", beanDefinitionBuilder.getBeanDefinition()); 此时如果没有更改别的代码直接运行的话,是会报如下错误的: Exception代码 Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.kfit.demo.service.TestService] is defined: expected single matching bean but found 2: testService1,testService ​ 大体意思就是在getBean的时候,找到了两个bean,这时候就不知道要获取哪个了,所以在获取的时候,我们就要指定我们是要获取的testService还是testService1,只需要修改一句代码: 将代码: Java代码 TestService testService =ctx.getBean(TestService.class); 修改为: Java代码 TestService testService =ctx.getBean("testService"); (4)动态删除; ​ 相对于动态注入,动态删除就很简单了,直接奉上代码: Java代码 //删除bean. defaultListableBeanFactory.removeBeanDefinition("testService");

优秀的个人博客,低调大师

更深入的理解 Python 中的迭代

Visual Studio 的应用周期管理Application Lifecycle Management(ALM)项目 —— Ranger 是一个志愿者社区,它提供专业的指导、实践经验、以及开发者社区的漏洞修补解决方案。它创建于 2006 年,作为微软内部社区去 “将产品组与大家相连接,并去除推广阻力”。 在 2009 时,社区已经有超过 200 位成员,这导致了协作和计划面临很大的挑战,在依赖和手工流程上产生了瓶颈,并导致了开发者社区不断增加的延迟和各种报怨。在 2010 时,计划进一步去扩充包括微软最有价值专家(MVP)在内的分布在全球的社区。 这个社区被分割成十几个活跃的团队。每个团队都致力于通过它的生命周期去设计、构建和支持一个指导或处理项目。在以前,团队的瓶颈在团队管理级别上,原因是严格的、瀑布式的流程和高度依赖一个或多个项目经理。在制作、发布和“为什么、做什么、和怎么做”驱动的决定上,项目经理都要介入其中。另外,缺乏一个实时的指标阻止了团队对他们的解决方案效率的监控,以及对来自社区的关于 bug 和常见问题的关注。 是时候去寻找一些做好这些事情的方法了,更好地实现开发者社区的价值。 DevOps 去“灭火” “DevOps 是人员、流程和产品的结合,使我们的最终用户能够持续传递价值。” --Donovan Brown 为解决这些挑战,社区停止了所有对新项目的冲刺,去探索敏捷实践和新产品。致力于使社区重新活跃起来,为找到促进自治、掌控、和目标的方法,正如在 Daniel H. Pink 的书《Drive》中所说的那样,对僵化的流程和产品进行彻底的改革。 “成熟的自组织、自管理和跨职能团队,在自治、掌控和目标上茁壮成长。" --Drive, Daniel H. Pink. 从文化开始 —— 人 —— 第一步是去拥抱 DevOps。社区实现了 Scrum 框架,使用 kanban 去提升工程化流程,并且通过可视化去提升透明度、意识和最重要的东西 —— 信任。使用自组织团队后,传统的等级制度和指挥系统消失了。自管理促使团队去积极监视和设计它们自己的流程。 在 2010 年 4 月份,社区再次实施了另外的关键一步,切换并提交它们的文化、流程、以及产品到云上。虽然开放的“为社区而社区”的核心 解决方案 仍然是指导和补充,但是在开源解决方案(OSS)上大量增加投资去研究和共享 DevOps 转换的成就。 持续集成(CI)和持续交付(CD)使用自动化流水线代替了死板的人工流程。这使得团队在不受来自项目经理的干预的情况下为早期问题和早期应用者部署解决方案。增加遥测技术可以使团队关注他们的解决方案,以及在用户注意到它们之前,检测和处理未知的问题。 DevOps 转变是一个持续进化的过程,通过实验去探索和验证人、流程和产品的改革。最新的试验引入了流水线革新,它可以持续提升价值流。自动扫描组件、持续地以及静默地检查安全、协议和开源组件的品质。部署环和特性标志允许团队对所有或者特定用户进行更细粒度的控制。 在 2017 年 10 月,社区将大部分的私有版本控制仓库转移到 GitHub 上。将所有仓库转移所有者和管理职责到 ALM DevOps Rangers 社区,给团队提供自治和机会,去激励更多的社区对开源解决方案作贡献。团队被授权向他们的最终用户交付质量和价值。 好处和成就 拥抱 DevOps 使 Ranger 社区变得更加敏捷,实现了对市场的快速反应和快速学习和反应的流程,减少了宝贵的时间投入,并宣布自治。 下面是从这个转变中观察到的一个列表,排列没有特定的顺序: 自治、掌控和目标是核心。 从可触摸的和可迭代的东西开始 —— 避免摊子铺的过大。 可触摸的和可操作的指标很重要 —— 确保不要掺杂其它东西。 人(文化)的转变是最具挑战的部分。 没有蓝图;任何一个组织和任何一个团队都是独一无二的。 转变是一个持续的过程。 透明和可视非常关键。 使用工程化流程去强化预期行为。 转换变化表: < 如显示不全,请左右滑动 > 过去 当前 想象 分支 服务于发布隔离 特性 主分支 构建 手动且易错 自动而一致 问题检测 来自用户 主动感知 问题解决 几天到几周 几分钟到几天 几分钟 计划 详细的设计 原型与故事板 流程管理 2 个流程经理(PM) 0.25 个 PM 0.125 个 PM 发布终止 6 到 12 个月 3 到 5 周期 每个周期 发布 手动且易错 自动而一致 周期 1 个月 3 周 团队规模 10 到 15 2 到 5 构建时间 几小时 几秒钟 发布时间 几天 几分钟 但是,我们还没有做完,相反,我们就是一个令人兴奋的、持续不断的、几乎从不结束的转变的一部分。 原文发布时间为:2018-05-26 本文作者:Willy Schaub 本文来自云栖社区合作伙伴“Linux中国开源社区”,了解相关信息可以关注“Linux中国开源社区”。

优秀的个人博客,低调大师

深入理解PHP之require/include顺序

作者:Laruence() 本文地址:http://www.laruence.com/2010/05/04/1450.html 转载请注明出处 在大型的Web项目中, include_path是一个模块化设计的根本中的根本(当然,现在也有很多基于autoload的设计, 这个不影响本文的探讨), 但是正是因为include_path, 经常会让我们遇到一些因为没有找到正确的文件而导致的看似”诡异”的问题. 也就有了如下的疑问: include_path是怎么起作用的? 如果有多个include_path顺序是怎么样的? 什么情况下include_path不起作用? 今天, 我就全面的介绍下这个问题, 先从一个例子开始吧. 如下的目录结构: root ├ 1.php ├ 3.php └ subdir ├ 2.php └ 3.php 在1.php中: <?php ini_set("include_path", ".:path_to_subdir"); require("2.php"); ?> 而在2.php中: <?php require("3.php"); ?> 而在root目录下的3.php打印出”root”, 在subdir目录下的3.php打印出”subdir”; 现在, 我的问题来了: 1. 当在root目录下运行1.php, 会得到什么输出? 2. 在subdir下运行上一级目录的1.php, 有会得到什么输出? 3. 当取消include_path中的当前目录path(也就是include_path=”path_to_subdir”), 上面俩个问题又会是什么输出? PHP中的include_path PHP在遇到require(_once)/include(_once)的指令的时候, 首先会做如下的判断: 要包含的文件路径是绝对路径么? 如果是, 则直接包含, 并结束. 如果不是, 进入另外的逻辑(经过多次调用, 宏展开后进入_php_stream_fopen_with_path)寻找此文件. 接下来, 在_php_stream_fopen_with_path中, 会做如下判断: 要包含的文件路径是相对路径么(形如./file, ../dir/file, 以下用"目录相对路径代替")? 如果是, 则跳过include_path的作用逻辑, 直接解析相对路径(随后单独介绍). 会根据include_path,和当前执行文件的path组成一个待选的目录列表, 比如对于文章前面的例子来说, 会形成一个如下的待选列表 ".:path_to_subdir:current_script_dir" 然后, 依次从待选列表头部开始, 根据DEFAULT_DIR_SEPARATOR(本文的环境是”:”)取出待选列表中的一个路径, 然后把要包含的文件名附加在这个路径后面, 进行尝试. 如果成功包含, 则返回, 否则继续下一个待选路径. 到现在为止, 我们已经可以回答我开头提出的3个问题了. 1. 因为在root目录下执行, 所以在1.php中包含2.php的时候, include_path的第二个待选路径起了作用(path_to_subdir), 找到了path_to_subdir/2.php, 而在2.php包含3.php的时候, 当前工作目录是root下, 所以在包含3.php的时候, include_path的第一个待选路径”.”(当前工作目录)下就找到的匹配的文件, 所以得到的输出是”root”. 2. 同1, 只不过当前的路径是subdir, 所以得到的输出是”subdir”. 3. 因为没有了当前路径为include_path, 所以在root目录下运行的时候2.php中包含3.php的时候, 是path_to_subdir起了作用, 所以无论在root还是subdir都将得到”subdir”的输出.而如果在2.php中清空include_path, <?php ini_set("include_path", ''); require("3.php"); ?> 那么将会是current_script_dir起作用, 而这个时候current_script_dir是2.php的路径, 所以还是会得到”subdir”的输出. 目录相对路径 在使用目录相对路径的情况下, 相对路径的基点, 永远都是当前工作目录. 为了说明在目录相对路径下的情况,我们再看个列子, 还是上面的目录结构, 只不过1.php变成了: <?php ini_set("include_path", "/"); require("./subdir/2.php"); ?> 2.php变成了: <?php require("./3.php"); ?> 如果在root目录下执行, 2.php中寻找3.php将会在当前目录的相对路径下寻找, 所以得到的输出是”root”, 而如果是在subdir下执行上一级目录的1.php(php -f ../1.php), 将会因为在subdir下找不到”./subdir/2.php”而异常退出. 后记 1. 因为使用include_path和相对路径的情况下, 性能会和寻找的次数有关, 最坏的情况下, 如果你有10个include_path, 那么最多可能会重试11次才能找到要包含的文件, 所以, 在能使用绝对路径的情况下最好使用绝对路径. 2. 因为目录相对路径的basedir, 永远都是当前工作路径, 如果要使用, 需要和实际部署路径相关, 所以实际使用的很少(当然, 也有借助chdir来完成的模块). 3. 在模块化的系统设计中, 一般应该在模块内, 通过获取模块的部署路径(dirname(__FILE__), php5.3以后更是提供了__DIR__常量)从而使用绝对路径.谋胆并重

优秀的个人博客,低调大师

深入理解JVM(七)——Class文件结构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34173549/article/details/79612685 什么是JVM的“无关性”? Java具有平台无关性,也就是任何操作系统都能运行Java代码。之所以能实现这一点,是因为Java运行在虚拟机之上,不同的操作系统都拥有各自的Java虚拟机,因此Java能实现“一次编写,处处运行”。 而JVM不仅具有平台无关性,还具有语言无关性。平台无关性是指不同操作系统都有各自的JVM,而语言无关性是指Java虚拟机能运行除Java以外的代码! 这听起来非常惊人,但JVM对能运行的语言是有严格要求的。首先来了解下Java代码的运行过程。 Java源代码首先需要使用Javac编译器编译成class文件,然后启动JVM执行class文件,从而程序开始运行。也就是JVM只认识class文件,它并不管何种语言生成了class文件,只要class文件符合JVM的规范就能运行。因此目前已经有Scala、JRuby、Jython等语言能够在JVM上运行。它们有各自的语法规则,不过它们的编译器都能将各自的源码编译成符合JVM规范的class文件,从而能够借助JVM运行它们。 纵观Class文件结构 class文件是二进制文件,它的内容具有严格的规范,文件中没有任何空格,全是连续的0/1。class文件中的所有内容被分为两种类型:无符号数 和 表。- 无符号数它表示class文件中的值,这些值没有任何类型,但有不同的长度。根据这些值长度的不同分为:u1、u2、u4、u8,分别代表1字节的无符号数、2字节的无符号数、4字节的无符号数、8字节的无符号数。- 表class文件中所有数据(即无符号数)要么单独存在,要么由多个无符号数组成二维表。即class文件中的数据要么是单个值,要么是二维表。 class文件的组织结构 魔数 本文件的版本信息 常量池 访问标志 类索引 父类索引 接口索引集合 字段表集合 方法表集合 Class文件的构成1:魔数 class文件的头4个字节称为魔数,用来表示这个class文件的类型。 魔数的作用就相当于文件后缀名,只不过后缀名容易被修改,不安全,因此在class文件中标示文件类型比较合适。 class文件的魔数是用16进制表示的“CAFEBABE”,非常具有浪漫主义色彩,谁说程序员的情商都很低! Class文件的构成2:版本信息 紧接着魔数的4个字节是版本号。它表示本class中使用的是哪个版本的JDK。 在高版本的JVM上能够运行低版本的class文件,但在低版本的JVM上无法运行高版本的class文件,即使该class文件中没有用到任何高版本JDK的特性也无法运行! Class文件的构成3:常量池 1. 什么是常量池? 紧接着版本号之后的就是常量池。常量池中存放两种类型的常量: 字面值常量字面值常量即我们在程序中定义的字符串、被final修饰的值。 符号引用符号引用就是我们定义的各种名字: 类和接口的全限定名 字段的名字 和 描述符 方法的名字 和 描述符 2. 常量池的特点 常量池长度不固定常量池的大小是不固定的,因此常量池开头放置一个u2类型的无符号数,用来存储当前常量池的容量。JVM根据这个值就知道常量池的头尾来。注:这个值是从1开始的,若为5表示池中有4个常量。 常量池中的常量由而为表来表示常量池开头有个常量池容量计数器,接下来就全是一个个常量了,只不过常量都是由一张张二维表构成,除了记录常量的值以外,还记录当前常量的相关信息。 常量池是class文件的资源仓库 常量池是与本class中其它部分关联最多的部分 常量池是class文件中空间占用最大的部分之一 3. 常量池中常量的类型 刚才介绍了,常量池中的常量大体上分为:字面值常量 和 符号引用。在此基础上,根据常量的数据类型不同,又可以被细分为14种常量类型。这14种常量类型都有各自的二维表示结构。每种常量类型的头1个字节都是tag,用于表示当前常量属于14种类型中的哪一个。 以CONSTANT_Class_info常量为例,它的二维表示结构如下:CONSTANT_Class_info表: 类型 名称 数量 u1 tag 1 u2 name_index 1 tag表示当前常量的类型(当前常量为CONSTANT_Class_info,因此tag的值应为7,表示一个类或接口的全限定名);name_index表示这个类或接口全限定名的位置。它的值表示指向常量池的第几个常量。它会指向一个CONSTANT_Utf8_info类型的常量,它的二维表结构如下:CONSTANT_Utf8_info表: 类型 名称 数量 u1 tag 1 u2 length 1 u1 bytes length CONSTANT_Utf8_info表示字符串常量;tag表示当前常量的类型,这里应该是1;length表示这个字符串的长度;bytes为这个字符串的内容(采用缩略的UTF8编码) 问:为什么Java中定义的类、变量名字必须小于64K?类、接口、变量等名字都属于符号引用,它们都存储在常量池中。而不管哪种符号引用,它们的名字都由CONSTANT_Utf8_info类型的常量表示,这种类型的常量使用u2存储字符串的长度。由于2字节最多能表示65535个数,因此这些名字的最大长度最多只能是64K。 问:什么是UTF-8编码?什么是缩略UTF-8编码?前者每个字符使用3个字节表示,而后者把128个ASKII码用1字节表示,某些字符用2字节表示,某些字符用3字节表示。 Class文件的构成4:访问标志 在常量池之后是2字节的访问标志。访问标志是用来表示这个class文件是类还是接口、是否被public修饰、是否被abstract修饰、是否被final修饰等。由于这些标志都由是/否表示,因此可以用0/1表示。访问标志为2字节,可以表示16位标志,但JVM目前只定义了8种,未定义的直接写0. Class文件的构成5:类索引、父类索引、接口索引集合 类索引、父类索引、接口索引集合是用来表示当前class文件所表示类的名字、父类名字、接口们的名字。它们按照顺序依次排列,类索引和父类索引各自使用一个u2类型的无符号常量,这个常量指向CONSTANT_Class_info类型的常量,该常量的bytes字段记录了本类、父类的全限定名。由于一个类的接口可能有好多个,因此需要用一个集合来表示接口索引,它在类索引和父类索引之后。这个集合头两个字节表示接口索引集合的长度,接下来就是接口的名字索引。 Class文件的构成6:字段表的集合 1. 什么是字段表集合? 接下来是字段表的集合。字段表集合用于存储本类所涉及到的成员变量,包括实例变量和类变量,但不包括方法中的局部变量。每一个字段表只表示一个成员变量,本类中所有的成员变量构成了字段表集合。 2. 字段表结构的定义 类型 名称 数量 u2 access_flags 1 u2 name_index 1 u2 descriptor_index 1 u2 attributes_count 1 attribute_info attributes attributes_count access_flags字段的访问标志。在Java中,每个成员变量都有一系列的修饰符,和上述class文件的访问标志的作用一样,只不过成员变量的访问标志与类的访问标志稍有区别。 name_index本字段名字的索引。指向一个CONSTANT_Class_info类型的常量,这里面存储了本字段的名字等信息。 descriptor_index描述符。用于描述本字段在Java中的数据类型等信息(下面详细介绍) attributes_count属性表集合的长度。 attributes属性表集合。到descriptor_index为止是字段表的固定信息,光有上述信息可能无法完整地描述一个字段,因此用属性表集合来存放额外的信息,比如一个字段的值。(下面会详细介绍) 3. 什么是描述符? 成员变量(包括静态成员变量和实例变量) 和 方法都有各自的描述符。对于字段而言,描述符用于描述字段的数据类型;对于方法而言,描述符用于描述字段的数据类型、参数列表、返回值。 在描述符中,基本数据类型用大写字母表示,对象类型用“L对象类型的全限定名”表示,数组用“[数组类型的全限定名”表示。描述方法时,将参数根据上述规则放在()中,()右侧按照上述方法放置返回值。而且,参数之间无需任何符号。 4. 字段表集合的注意点 一个class文件的字段表集合中不能出现从父类/接口继承而来字段; 一个class文件的字段表集合中可能会出现程序猿没有定义的字段如编译器会自动地在内部类的class文件的字段表集合中添加外部类对象的成员变量,供内部类访问外部类。 Java中只要两个字段名字相同就无法通过编译。但在JVM规范中,允许两个字段的名字相同但描述符不同的情况,并且认为它们是两个不同的字段。 Class文件的构成7:方法表的集合 在class文件中,所有的方法以二维表的形式存储,每张表来表示一个函数,一个类中的所有方法构成方法表的集合。方法表的结构和字段表的结构一致,只不过访问标志和属性表集合的可选项有所不同。 类型 名称 数量 u2 access_flags 1 u2 name_index 1 u2 descriptor_index 1 u2 attributes_count 1 attribute_info attributes attributes_count 方法表的属性表集合中有一张Code属性表,用于存储当前方法经编译器编译过后的字节码指令。 方法表集合的注意点 如果本class没有重写父类的方法,那么本class文件的方法表集合中是不会出现父类/父接口的方法表; 本class的方法表集合可能出现程序猿没有定义的方法编译器在编译时会在class文件的方法表集合中加入类构造器和实例构造器。 重载一个方法需要有相同的简单名称和不同的特征签名。JVM的特征签名和Java的特征签名有所不同: Java特征签名:方法参数在常量池中的字段符号引用的集合 JVM特征签名:方法参数+返回值 Class文件的构成8:属性表的集合

优秀的个人博客,低调大师

一篇文章完全理解virtualenv

1.virtualenv介绍 在python开发中,我们可能会遇到一种情况,就是当前的项目依赖的是某一个版本,但是另一个项目依赖的是另一个版本,这样就会造成依赖冲突,而virtualenv就是解决这种情况的,virtualenv通过创建一个虚拟化的python运行环境,将我们所需的依赖安装进去的,不同项目之间相互不干扰,如下所示。 2.安装virtualenv 安装virtualenv比较简单,无论是在linux系统上还是windows上都可以通过pip来进行安装,如下: pip install virtualenv 如果安装不成功也可以下载virtualenv源码package,通过执行setup.py来安装virtualenv。同时因为python2和python3的不同,因此也有了两种pip和pip3两种python包安装工具,而两

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册