聊聊Java8之后的JDK升级内容(看这一篇就够了)

背景

2022年Spring6和 SpringBoot3相继推出,在此之前,Java社区一直是"新版任你发,我用Java 8",不管新版本怎么出,很少有人愿意升级。

这一次,Spring 直接来了个大招,SpringBoot3和Spring6的最低依赖就是JDK17。 跨过 JDK 8-16,直接升级到 JDK 17。那么为什么是 JDK 17呢?

这么多新版本的 JDK,而且2022年还会推出 JDK 18 和 JDK 19,为什么 Spring 选择了 JDK 17呢。

主要是因为他是一个 Oracle官宣可以免费商用的LTS版本,所谓 LTS,是 Long Term Support,也就是官方保证会长期支持的版本。

下表是 Oracle 官方提供的 JDK 支持计划:
JDK 支持计划
可以看得到,JDK 17 最多可以支持到 2029 年 9 月份。按照技术更新迭代的速度,这次免费商用 8 年可谓是良苦用心,为的就是让使用者放心大胆地将 JDK 升级到 JDK 17(不过JDK 8 支持的时间更长,可以延长到 2030 年 12 月,JDK8可谓是YYDS!)

现在,这种平衡或将被打破。因为 Java 届的霸主框架 SpringBoot,选择了最小支持的 Java LTS 版本,就是最新的 Java 17。

那么接下来,让我们看看从 JDK 8 到 JDK 17,Java 社区 8 年努力的成果有哪些?

从 JDK 8 到 JDK 17 的新特性

JDK8 回顾

新语法

  • Lambda 表达式和函数式接口
  • 接口默认方法和静态方法

核心类库

  • Optional Java 8也将Optional加入了官方库。 解决NullPointerException。
  • Stream流,新增的Stream API(java.util.stream将生成环境的函数式编程引入了Java库中。
  • Date/Time API

Clock 类LocalDate、LocalTime和LocalDateTime。
Clock.systemUTC() 等价于
System.currentTimeMillis() 和 TimeZone.getDefault()。
ZoneDateTime
Duration类

  • Base64 引入官方库 java.util.Base64

JVM

  • 使用 Metaspace (JEP 122)代替持久代(PermGen space)
    疑问为何要在jdk8中消除永久代嘞 作者动机是什么?

JEP 122 消除永久代 https://openjdk.org/jeps/122
动机:这是 JRockit 和 Hotspot 融合工作的一部分。
JRockit 客户不需要配置永久代(因为 JRockit 没有永久代) ,并且习惯于不配置永久代。
JRockit说明:Oracle JRockit (原来的 Bea JRockit)系列产品是一个全面的Java运行时解决方案组合,包括了行业最快的标准Java解决方案。
该调整面临风险:将内部字符串和类静态值移动到 Java 堆可能会导致内存不足异常或 GC 数量的增加。可能需要用户对 -Xmx 进行一些调整。

JDK9

GC

  • 默认GC是使用G1
  • 弃用的垃圾收集器 (GC) 组合
    ParNew + SerialOld
    DefNew + CMS
  • Deprecates 弃用并发标记扫描 (CMS) 垃圾收集器 JEP 291 https://openjdk.org/jeps/291
    为何要弃用CMS?

    (G1)垃圾收集器完全替代 CMS 的大多数用法。
    废弃并发标记清除(CMS)垃圾收集器,以便在将来的主要版本中停止对它的支持。
    研发者动机和目标:加速 HotSpot 中其他垃圾收集器的开发。减少 GC 代码库的维护负担,并加速新的开发。

核心类库

  • HTTP/2客户端

Java 9 引入了新的 API,它使用起来更干净、更清晰,并且还增加了对 HTTP/2 的支持。

  • Collection(集合) API更新
//在 Java 9 中为集合的创建增加了静态工厂创建方式,也就是 of 方法,通过静态工厂 of 方法创建的集合是只读集合,里面的对象不可改变。
        List<String> namesList = List.of("Lokesh", "Amit", "John");
        Set<String> namesSet = Set.of("Lokesh", "Amit", "John");
        Map<String, String> namesMap = Map.ofEntries(
                         Map.entry("1", "Lokesh"),
                         Map.entry("2", "Amit"),
                         Map.entry("3", "Brian"));
  • Stream(流) API改进

Java 9 引入了两种与 Streams 交互的新方法,即takeWhile/dropWhile方法。

	/**
     * 新方法takeWhile。dropWhile允许开发者基于谓词获取流的一部分
     */
    public static void main(String[] args) {

        List<String> alphabets = List.of("a", "b", "c", "d", "e", "f", "g", "h", "i");
        List<String> one = alphabets
                .stream()
                .takeWhile(s -> !s.equals("d"))  //takeWhile: 从头开始筛选,遇到不满足的就结束了。
                .collect(Collectors.toList());
        //打印出:[a, b, c]
        System.out.println(one);

        List<String> alphabets2 = List.of("a", "b", "c", "d", "e", "f", "g", "h", "i");
        List<String> subset2 = alphabets2
                .stream()
                .dropWhile(s -> !s.equals("d")) //odropWhile: 从头开始删除,遇到不满足的就结束了。
                .collect(Collectors.toList());
        //打印出:[d, e, f, g, h, i]
        System.out.println(subset2);


        /**
         * TODO 爆赞~~~~ 好用
         * 此外,它还添加了两个重载方法,即ofNullable和iterate方法,类似option的用法。
         */
        //在 Java 8 之前,流中不能有null值。它会导致NullPointerException.
        // 从 Java 9 开始,Stream.ofNullable()方法允许您创建一个单元素流,该流包装一个不为null的值,否则为空流。.
        Stream.ofNullable(alphabets2).collect(Collectors.toList());

        /**
         * 在 Stream 增强之外,还增强了 Optional ,Optional 增加了可以转换成 Stream 的方法。
         */
        Stream<Integer> s = Optional.of(1).stream();
        s.forEach(System.out::print);
    }
  • 多版本Jar文件
  • @Deprecated 注解更改
	/**
     * 从 Java 9 开始,@Deprecated注解将具有两个属性,即forRemoval和since.
     * @eprecated  forRemoval – 指示带注释的元素是否会在未来版本中被删除。
     *             since - 它返回注释元素被弃用的版本。
     */
    @Deprecated(forRemoval = true,since = "jdk9")
  • 模块化

JPMS(Java Platform Module System)是Java 9发行版的核心亮点。
JDK 9 附带了大约 92 个模块(在 GA 版本中可以进行更改)。Java 9 Module System有一个"java.base"模块。它被称为基本模块。它是一个独立的模块,不依赖于任何其他模块。默认情况下,所有其他模块都依赖于"java.base"。
个各模块通常只是一个 jar 文件,在根目录下有一个文件module-info.class。
要使用模块,请将 jar 文件包含到modulepath而不是classpath. 添加到类路径的模块化 jar 文件是普通的 jar 文件,module-info.class文件将被忽略。

  • 接口私有方法
/**
 * Java 8 允许在接口中编写默认方法,这是一个广受欢迎的功能。从 Java 9 开始,你可以在接口中包含私有方法。
 * 私有接口方法
 *
 * 规则:
 * 私有接口方法不能是抽象的。
 * 私有方法只能在接口内部使用。
 * 私有静态方法可以在其他静态和非静态接口方法中使用。
 * 私有非静态方法不能在私有静态方法中使用
 *  */
public interface Custom {

    default int addEvenNumbers(int... nums) {
        return add(n -> n % 2 == 0, nums);
    }

    default int addOddNumbers(int... nums) {
        return add(n -> n % 2 != 0, nums);
    }

    //私有的底层方法
    private int add(IntPredicate predicate, int... nums) {
        return IntStream.of(nums)
                .filter(predicate)
                .sum();
    }

}

JDK10

描述

  • 自从 Java 9 开始,Oracle 调整了 Java 版本的发布策略,不再是之前的 N 年一个大版本,取而代之的是 6 个月一个小版本,三年一个大版本,这样可以让 Java 的最新改变迅速上线,而小版本的维护周期缩短到下个版本发布之前,大版本的维护周期则是 3 年之久。而 10 就是这么一个小版本,因为 Java 的后续版本基本都会包含之前新特性。

新语法

  • JEP 286 var 局部类型推断

JEP 286 var 局部类型推断
让 Java 可以像 Js 里的 var 或者其他语言的 auto 一样可以自动推断数据类型。
这其实只是一个新的语法糖,底层并没有变化,在编译时就已经把 var 转化成具体的数据类型了,但是这样可以减少代码的编写。
让 Java 可以像 Js 里的 var 或者其他语言的 auto 一样可以自动推断数据类型。

  var hashMap = new HashMap<String, String>();
    hashMap.put("a","b");
    var string = "hello java 10";
    var stream = Stream.of(1, 2, 3, 4);
    var list = new ArrayList<String>();
    //编译后一样的
    /**
     *         HashMap<String, String> hashMap = new HashMap();
     *         hashMap.put("a", "b");
     *         String string = "hello java 10";
     *         Stream<Integer> stream = Stream.of(1, 2, 3, 4);
     *         new ArrayList();
     *
     *         缺点也很明显:
     *         一眼并不能看出 result 的数据类型。
     */

GC

  • G1 的自动切换并行收集

早在 Java 9 时就已经引入了 G1 垃圾收集器,G1 的优点很多。而在 Java 10 中还是做了小小调整,当G1 的并发收集线程不能快速的完成full GC 时,就会自动切换到并行收集,这可以减少在最坏情况下的 GC 速度。
也可以看出 G1的作者 其实在fullGC 的情况上处理 还在优化,因为他们设计G1的初衷 就是极力避免full GC的出现。但是当并发收集不能足够快地回收内存时,就会引发full GC 垃圾收集。G1的full GC的旧 实现 使用的单线程-标记-清理-压缩(a single threaded-mark-sweep-compact )算法。在 JEP 307中 作者决定采用 并行-清除-压缩(parallelize the mark-sweep-compact ) 算法 ,并使用与 Young 和Mixed 集合 相同数量的线程来进行处理。
线程数量可以通过命令 : -XX:ParallelGCThreads 来控制。(但是这也会影响我们的线程数量)
风险 设定: G1采用并发 收集fullGC的使用资源 肯定要比之前单线程 的时候更耗资源。
官文资料 :https://bugs.java.com/view_bug.do?bug_id=JDK-8172890

实验性:

  • GraalVM首次引入

这是一个实验性的 JIT 编译器。Java 10 中最具有未来感的引入。
Graal 其实在 Java 9 中就已经引入了,它带来了 Java 中的 AOT (Ahead Of Time 提前编译)编译,还支持多种语言,如 Js、Python、Ruby、R、以及其他基于 JVM (如 Java、Kotlin)的和基于 LLVM (如 C、C++)的语言。
基本上由Java语言编写。

核心类库

  • Collection新增方法 Collection.copyOf 复制得到一个不可改变集合
    不可变工具集合是为何不可变嘞?
 		List<String> listCopy = List.copyOf(list);
        //Arrays.asList()
        Optional.of(list).orElseThrow();

        /**
         * 不可变工具集合是为何不可变嘞?
         * ListN  实体  ImmutableCollections.ListN  内部类
         * 类似Arrays.asList()方法  返回的不是我们认为list对象 而是一个内部类。
         */
  • Optional新增方法 orElseThrow 方法 value = null 直接抛异常。它是现有 get 方法的同义词,是现有 get 方法的首选替代方法。
  • Stream 新增方法 转成不可变集合方法。

list.stream().collect(Collectors.toUnmodifiableList());

runtime

  • 字节码生成已经得到改进,增强了 For 循环
 		/**
         * 编译后:
         * Iterator i$ = data.iterator();
         * for (; i$.hasNext(); ) { String b = (String)i$.next(); } b = null; i$ = null; }
         * *//*
        Iterator i$ = data.iterator();
        for (; i$.hasNext(); ) {
            String b = (String) i$.next();
        }
        b = null;
        i$ = null;
        }*/

这行编译后的代码有什么特点 ?
在 for 循环之外声明迭代器变量可以在不再使用它时立即为其赋值 null。 这使得 GC 可以访问它,然后 GC 可以处理掉未使用的内存。当增强的 for 循环中的表达式是一个数组时,也会执行类似的操作。

衍生问题 研发者为何要在for循环后做该编译优化操作?
个人理解 是为了配合GC ,GC中的 Safe Point 安全点的设置。
安全点位置的选取基本上是以 “是否具有让程序长时间执行的特征” 为标准 进行选定的,因为每条指令执行的时间都非常短暂,程序不太可能因为指令流长度太长这样的原因而 长时间执行, “长时间执行” 的最明显特征就是指令序列的复用,例如方法调用、循环跳转、异常跳转 等都属于指令序列复用,所以只有具有这些功能的指令才会产生安全点。
只有到达某些点才可以进行GC操作,这些点称作安全点(Safepoint),比如,循环的末尾、方法临返回前/调用方法的call指令后、可能跑异常的位置等。

安全点:在OopMap的协助下,HotSpot可以快速准确地完成GC Roots枚举,但随之而来的一个现实问题:可能导致引用关系变化,或者说导致OopMap内容变化的指令非常之多,如果为每一条指令都生成对应的OopMap,那将会需要大量的额外存储空间,这样垃圾收集伴随而来的空间成本就会非常高昂。
在实际上,也的确没有为每条指令都生成OopMap,只是在“特定的位置”记录 了这些信息,这些位置被称为安全点(Safepoint)。
有了安全点的设定,也就决定了用户程序执行时 并非在代码指令流的任意位置都能够停顿下来开始垃圾收集,而是强制要求必须执行到达安全点后才能够暂停。

  • 类数据共享

JVM 启动时有一步是需要在内存中加载类,而如果有多个 jar,加载第一个 jar 的速度是最慢的。这就延长了程序的启动时间,为了减少这个时间,Java 10 引入了应用程序类数据共享(CDS)机制,它可以把你想共享的类共享在程序之间,使不同的 Java 进程之间共享这个类来减少这个类占用的空间以及加载速度。

JDK11

描述

  • Java 11 是 Java 8 之后的第一个 LTS(长期支持) 版本。
    核心类库

  • String API的改动

  		/**
         * =================String 新增api功能========================================
         */
        String str= "aa\nbb";

        //判空,blank
        System.out.println(str.isBlank());

        //该方法是根据 换行符 \n  或者回车  \r 或者回车换行  \r\n  进行分割
        Stream<String> lines = str.lines();
        lines.forEach(System.out::println);

        //复制字符串
        String str1= "abc";
        String repeat = str1.repeat(2);
        System.out.println(repeat);

        /**
         * 输出
         * false
         * aa
         * bb
         * abcabc
         */

        // 去除前后空白
        String strip = "     string字符  ";
        System.out.println("==" + strip.trim() + "==");
        // 去除前后空白字符,如全角空格,TAB
        System.out.println("==" + strip.strip() + "==");
        // 去前面空白字符,如全角空格,TAB
        System.out.println("==" + strip.stripLeading() + "==");
        // 去后面空白字符,如全角空格,TAB
        System.out.println("==" + strip.stripTrailing() + "==");

        // 输出
        // ==  string字符  ==
        // ==string字符==
        // ==string字符  ==
        // ==     string字符==
        /**这里注意,trim 只能去除半角空格,而 strip 是去除各种空白符。*/
  • File API改动
 		/**=================
         * File API改动
         * 读写文件变得更加方便。
         * ========================================*/
        // 创建临时文件
        Path path = Files.writeString(Files.createTempFile("test", ".txt"),  "https://www.baidu.com");
        System.out.println(path);
        // 读取文件
        // String ss = Files.readString(Path.of("file.json"));
        String s = Files.readString(path);
        System.out.println(s);

  • HTTP Client
		/**=================
         * HTTP Client
         * 在 Java 11 中 Http Client API 得到了标准化的支持。且支持 HTTP/1.1 和 HTTP/2 ,也支持 websockets。
         * http://openjdk.java.net/groups/net/httpclient/recipes-incubating.html
         * HTTPClient 已经在 Java11中标准化了。
         * ========================================*/

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://www.weibo.com"))
                .build();
        // 异步
        client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
                .thenApply(HttpResponse::body)
                .thenAccept(System.out::println)
                .join();

        // 同步
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());

新语法

  • Lambda 参数的局部变量语法
   		/**=================
         *
         * Lambda 局部变量推断
         * 集合jdk10引入的var语法,在jdk11的时候,这个语法糖可以在Lambda中进行使用。
         *
         * ========================================*/
        var hashMap = new HashMap<String, Object>();
        hashMap.put("A", "a");
        hashMap.put("B", "b");
        hashMap.forEach((var k, var v) -> {
            System.out.println(k + ": " + v);
        });
        //这里需要注意的是,(var k,var v) 中,k 和 v 的类型要么都用 var ,要么都不写,要么都写正确的变量类型。而不能 var 和其他变量类型混用。

runtime

  • 单命令运行 Java
  	/**
         *  单命令运行 Java
         *  之前运行一个java 程序
         *  1. javac 编译字节码
         *  2. java 运行程序class文件
         *
         * jdk11之后
         * $ java  xxx.java   即可运行
         */

实验性

  • 可伸缩的低延迟垃圾收集器 - 引入ZGC垃圾收集器 jdk11提出而已 仅支持在 Linux/x64上使用。

  • A No-Op (理解成无操作)垃圾收集器 -Epsilon GC
    Hotspot 编译器

  • 编译器线程的延迟分配

在默认打开的分层编译模式下,VM 在具有许多 CPU 的系统上启动大量编译器线程,而不管可用内存和编译请求的数量如何。因为线程即使在空闲时(几乎总是空闲)也会消耗内存,这导致资源的使用效率低下。
为了解决这个问题,实现进行了更改,以便在启动期间只启动每种类型的一个编译器线程,并动态处理进一步线程的启动和关闭。它由一个新的命令行标志控制,默认情况下是打开的。

JDK12

核心类库

  • 文件对比 Files.mismatch
 		/**
         * 文件对比 Files.mismatch
         * 对比两个文件内容是否一致,如果内容一致,会返回 -1  ,如果内容不同,会返回不同的字节开始位置。
         */
        // 创建两个临时文件
        Path aFile = Files.createFile(Paths.get("A.txt"));
        Path bFile = Files.createFile(Paths.get("B.txt"));

        // 写入相同内容
        Files.write(aFile,"123456".getBytes(), StandardOpenOption.WRITE);
        Files.write(bFile,"123456".getBytes(), StandardOpenOption.WRITE);
        long mismatch = Files.mismatch(aFile, bFile);
        System.out.println(mismatch);

        // 追加不同内容
        Files.write(aFile,"789".getBytes(), StandardOpenOption.APPEND);
        Files.write(bFile,"987".getBytes(), StandardOpenOption.APPEND);
        mismatch = Files.mismatch(aFile, bFile);
        System.out.println(mismatch);

        // 删除创建的文件
        aFile.toFile().deleteOnExit();
        bFile.toFile().deleteOnExit();
        //输出
        //-1  相同
        //6   6下标  从0开始的话的 也就是第七位开始不同 正好是上面追加的点
  • 数据格式处理NumberFormat
		/**
         *  紧凑数字格式化的支持 NumberFormat 添加了对紧凑形式的数字格式化的支持。紧凑型数字格式是指以简短或人类可读的形式表示数字。
         *  例如,在 en _ US 区域设置中,根据 NumberFormat 指定的样式,
         *  1000可以格式化为“1K”,1000000可以格式化为“1M”。风格。
         *  紧凑数字格式由 LDML 的紧凑数字格式规范定义
         */
        System.out.println("Compact Formatting is:");
        NumberFormat upvotes = NumberFormat.getCompactNumberInstance(new Locale("en", "US"), NumberFormat.Style.SHORT);

        System.out.println(upvotes.format(100));
        System.out.println(upvotes.format(1000));
        System.out.println(upvotes.format(10000));
        System.out.println(upvotes.format(100000));
        System.out.println(upvotes.format(1000000));

        // 设置小数位数
        upvotes.setMaximumFractionDigits(1);
        System.out.println(upvotes.format(1234));
        System.out.println(upvotes.format(123456));
        System.out.println(upvotes.format(12345678));
        //100
        //1K
        //10K
        //100K
        //1M
        //1.2K
        //123.5K
        //12.3M

GC

  • ZGC现在支持类卸载。

通过卸载未使用的类,可以释放与这些类相关的数据结构,从而降低应用程序的总体占用。
ZGC 中的类卸载同时进行,而不会停止 Java 应用程序线程的执行,因此对 GC 暂停时间没有影响。
默认情况下启用此特性,但可以使用命令行选项 -XX:-ClassUnload 禁用此特性。

预览功能

  • Switch 表达式 改进 case “A”, “B”, “C” -> “method”; 的形式,让Switch的代码编写变得更加优雅。

删除项

  • finalize 方法的删除

从 FileInputStream 和 FileOutputStream 中删除 finalize 方法, finalize 方法在 JDK9中不被推荐删除。jdk12中finalize方法被正式移除 。
删除 java.util 中的 finalize 方法。

JDK13

预览功能

  • Switch 表达式二次预览
  • 文本块首次预览
 		/**
         * 文本块 (jdk13 预览功能 )
         * 之前string中放入json 字符串 会出现大量的转义符,从 Java 13 开始你可以使用文本块的方式定义字符串了。
         * 向 Java 语言添加文本块。它避免了大多数转义序列的需要,自动以可预测的方式格式化字符串,并在需要时让开发人员控制格式。
         */
        String oldStr = "{\"name\":\"hh\",\"age\":18}";
        //终于不用写丑陋的长字符串了~~~~~~
        String str13= """
                {"name":"hh","age":18}
                """;

核心类库

  • 重新实现 Socket API
 		/**
         *  重新实现 Socket API
         *
         * java.net.Socket 和 java.net.ServerSocket 类早在 Java 1.0 时就已经引入了,
         * 它们的实现的 Java 代码和 C 语言代码的混合,维护和调试都十分不易;而且这个实现还存在并发问题,有时候排查起来也很困难。
         *
         * 底层 采用 NioSocketImpl 新的实现类 替代PlainSocketImpl
         *
         * 作者支持回退 -Djdk.net.usePlainSocketImpl 命令可以切回旧的实现。 还是很有考虑到的。
         */

        ServerSocket serverSocket = new ServerSocket(8000);
        Socket clientSocket = serverSocket.accept();
        // jdk 13 之后  底层类记载的是  sun.net.PlatformSocketImpl
        // jdk 13 之前  底层类记载的是  java.net.PlainSocketImpl

实验性

  • ZGC,归还未使用的内存

JEP 351: ZGC https://openjdk.org/jeps/351
在 Java 13 之前,ZGC 虽然在清理内存时导致的停顿时间非常少,但是即使内存已经长时间没有使用,ZGC 也不会将内存返还给操作系统,这对那些十分关注内存占用的应用程序非常不友好。
默认情况下启用此特性,但可以使用 -XX:-ZUncommit 显式禁用此特性。此外,内存不会被归还,因此堆大小会缩小到最小堆大小(- Xms)以下。这意味着,如果将 最小堆大小(- Xms)配置为等于最大堆大小(- Xmx),则将 隐式禁用 此特性。
-XX:ZUncommitDelay= (默认为300秒) 可以通过这个命令来控制 内存多少秒后 可以被归还。

GC

  • ZGC的新增功能
    添加 -XXSoftMaxHeapSize 标志

设置后,GC 将努力不使堆增长超过指定的大小,除非 GC 决定有必要这样做以避免 OutOfMemoryError。
不允许将软最大堆大小设置为大于最大堆大小(- Xmx)的值。如果未在命令行上设置,则默认为等于最大堆大小的值。

  • ZGC 支持的最大堆大小从4TB 增加到16TB。

JDK14

预览功能

  • instanceof 类型判断 (第一次预览)
  • Records class (第一次预览) 添加了一个新的 java.lang 类
  • 文本块 (二次预览)

实验性

  • Windows 上的 ZGC(实验性)
  • macOS 上的 ZGC(实验性)

新语法

  • 更有用的npe提示
 		/**
         * 更有用的 NullPointerExceptions提示  TODO 超级赞~~~
         */

        String str = "str";
        String ex = null;
        int length = str.length() + ex.length();
        System.out.println(length);
        //14 之前输出
        //Exception in thread "main" java.lang.NullPointerException
        //at dxt.learn.example.jdk14.JDK14Main.main(JDK14Main.java:36)

        //14 之后输出
        //Exception in thread "main" java.lang.NullPointerException:
        //	Cannot invoke "String.length()" because "ex" is null
        //at dxt.learn.example.jdk14.JDK14Main.main(JDK14Main.java:36)
  • Switch 表达式 正式发行
 		/**
         * Switch 表达式 (正式发行)
         * 也是很赞的功能  不用那么累赘的书写switch了
         *
         */

        // 通过传入月份,输出月份所属的季节  jdk 12的第一次预览
        String month12 = "may";
        String season12 = switch (month12) {
            case "march", "april", "may"            -> "春天";
            case "june", "july", "august"           -> "夏天";
            case "september", "october", "november" -> "秋天";
            case "december", "january", "february"  -> "冬天";
            default -> "month erro";
        };

        //jdk 13的第二次预览
        String month13 = "may";
        String season13 = switch (month13) {
            case "march", "april", "may":
                yield "春天";
            case "june", "july", "august":
                yield "夏天";
            case "september", "october", "november":
                yield "秋天";
            case "december", "january", "february":
                yield "冬天";
            default:
                yield "month error";
        };
        //jdk14 让这些 预览功能正式 发行  无需通过命令来开启功能

删除项

  • JEP 363: 移除 CMS 垃圾收集器

移除对 CMS(Concurrent Mark Sweep) 垃圾收集器的支持,其实早在 Java 9 就开始移除 CMS 垃圾收集器了,只不过在 Java 14 中被正式删除。

弃用

  • 弃用 ParallelScavenge + SerialOld GC 组合 https://openjdk.java.net/jeps/366,

由于使用场景不多,维护工作太大,废弃之。

  • 不推荐线程挂起/恢复

java.lang. Thread 和 java.lang. ThreadGroup 中的suspend() 和.resume()方法废弃。

JDK15

预览

  • Sealed Classes 密封类(二次预览)
  • instanceof 类型匹配 (二次预览)
  • Records class (二次预览)

新语法

  • 文本块 (正式发布) JDK13提出~JDK15发行
 		/**
         * 文本块 (正式发布)
         */
        String oldStr = "{\"name\":\"hh\",\"age\":18}";
        //终于不用写丑陋的长字符串了
        String str15= """
                {"name":"hh","age":18}
                """;
  • TreeMap 新的方法

putIfAbsent、computeIfAbsent、computeIfPresent、compute、merge

JVM

  • 禁用和废弃偏向锁(Biased Locking)

synchronized 同步时,有一套锁的升级机制,其中有一个锁机制就是偏向锁。
然而通过目前的 Java 开发环境来看,使用这些被 synchronized 同步的类的机会并不多,如开发者更喜欢使用 HashMap 或者 ArrayList 而非 HashTable 和 Vector。
性能提升程度和使用次都不太有用,而数偏向锁的引入增加了 JVM 的复杂性。
偏向锁被默认禁用,我们仍然可以使用 -XX:+UseBiasedLocking 启用偏向锁定,但它会提示 这是一个已弃用的 API。
偏向锁定可能会影响显示大量无争用同步的应用程序的性能。

GC

  • ZGC: 可扩展低延迟垃圾收集器(正式发布)

JDK11首次被引入 JDK15被正式发布.期间做过很多功能更新。启用命令: -XX:+UseZGC

JDK16

新语法

  • instanceof 模式匹配 (正式发行)
     	Object obj = new String();
        /**
         *  instanceof 类型匹配  正式发行 todo 也是好用的
         */
        if (obj instanceof String) {
            String s = (String) obj;
        }
        //当前的写法
        if (obj instanceof String s) {
            s.length();
            // Let pattern matching do the work!
            //.....
        }

        //public final boolean equals(Object o) {
        //    return (o instanceof Point other)
        //        && x == other.x
        //        && y == other.y;
        //}

  • Record Classes 记录类 (正式发行)
/**
 * JDK.16  JEP 395   https://openjdk.org/jeps/395
 *
 * 特点:
 *      Record是 Java 语言中的一种新类。
 *      它们充当不可变数据的透明载体,不像普通类那样拘谨。
 *      记录类有助于以比普通类更少的形式对普通数据聚合进行建模。
 * 动机:
 *    常见的抱怨是“Java 太冗长”或“太多仪式”。
 *     正确编写这样的数据载体类涉及大量低价值、重复、容易出错的代码:构造函数、访问器、equals、hashCode、toString等。
 *
 * 语法写法:
 *          写法:
 *          public record Teacher(String name, String className) {
 *
 *          }
 *
 *          编译后:
 *
 *          public record Teacher(String name, String className) {
 *           //隐式属性被final修饰
 *          private final String name;
 *          private final String className;
 *
 *
 *          Point(String name, String className) {
 *                  this.name= name;
 *                  this.className = className;
 *             }
 * }
 *
 */
@Slf4j
public record Teacher(String name, String className) {
    public String sayHi(){
        log.info("a");
        return "a";
    }

    /**
     * 本地记录类嵌套记录类的一个特例
     * 在以下示例中,商家和月销售数据的聚合使用本地记录类 建模MerchantSales。使用这个记录类提高了以下流操作的可读性:
     *
     * List<Merchant> findTopMerchants(List<Merchant> merchants, int month) {
     *
     *     // Local record 本地记录类
     *     record MerchantSales(Merchant merchant, double sales) {}
     *
     *     return merchants.stream()
     *         .map(merchant -> new MerchantSales(merchant, computeSales(merchant, month)))
     *         .sorted((m1, m2) -> Double.compare(m2.sales(), m1.sales()))
     *         .map(MerchantSales::merchant)
     *         .collect(toList());
     * }
     */

  public static void main(String[] args) {
      Teacher teacher = new Teacher("b","c");
      Class<? extends Teacher> aClass = teacher.getClass();
      System.out.println(aClass);
      /**
       * 两个新增的反射方法
       */
      RecordComponent[] recordComponents = aClass.getRecordComponents();
      System.out.println(aClass.isRecord());
      System.out.println(User.class.getSuperclass());


  }


}

  • Add Stream.toList() Method
  		/**
         *         ==============
         *         Add Stream.toList() Method  TODO 好用~~
         *`stream.collect(Collectors.toList())` 和 `stream.collect(Collectors.toSet())`
         * 最受欢迎的方法引来的简化和封装 。
         *
         *         ====================
         */
        record  User(String name){}
        List<User> users = new ArrayList<>();
        List<String> strings = users.stream().map(User::name).toList();
        // 等价 users.stream().map(User::name).collect(Collectors.toList());

预览

  • Sealed Classes(密封类)二次预览

GC

  • ZGC 并发栈处理 JEP 376. https://openjdk.org/jeps/376

ZGC 现在可以并发地处理线程堆栈。
这允许 ZGC 在并发阶段处理 JVM 中的所有根,而不需要STW
ZGC 暂停中完成的工作量现在变成了常量,通常不超过几百微秒。

Runtime

  • 弹性元空间

-XX:MetaspaceReclaimPolicy=(balanced|aggressive|none) 来控制
默认balanced:均衡会导致 VM 回收内存,同时保持最小的计算开销
aggressive:增加回收的速度,代价是更昂贵的记账

弃用

  • AOT 和 Graal JIT 实验特征的去除

AOT 和 JIT 编译器已在 JDK 16 中移除,本次在 JDK17 源代码中移除。

JDK17

描述

  • Java 17 在 2021 年 9 月 14 日正式发布,Java 17 是一个长期支持(LTS)版本。

新语法

  • Sealed Classes 在 Java SE 15 中首次预览,是此版本中的永久功能。
/**
 * JDK17
 * 密封类 :密封的类或接口只能由允许这样做的类和接口扩展或实现。
 * sealed 修饰符  密封类必须有子类
 * permits 指定许可
 *
 * 作者设计动机:
 *      限制一个接口或者一个类的扩展。
 *      超类应该可以被广泛访问,但不能被广泛扩展。
 *
 * JDK中的应用示例:AbstractStringBuilder  是以包私有的形式,两个子类:StringBuffer、StringBuilder。
 *
 * 密封类可类似枚举使用在Switch中作为case条件进行判断,无需进项 instance of 的语句判断并转换。
 */
public sealed class BaseObj permits BaseObj.SunObj {

    final class SunObj extends BaseObj implements BaseApi {

        @Override
        public void sayHi() {

        }
    }

    record  SunBo() implements BaseApi{

        @Override
        public void sayHi() {

        }
    }
    //应用在java.long.constant包中ConstantDesc

/**
 * JDK17
 * 密封类
 * sealed 修饰符  密封类必须有子类
 * permits 指定许可
 */
public sealed class BaseObj1  {
    //这种声明写法  可以省略掉  permits关键字
    //编译器可以自己识别推断出允许的子类
    //编译器可以判断出 baseObj1对象 允许三个sunObj的子类
    final class SunObj1 extends BaseObj1 {

    }

    final class SunObj2 extends BaseObj1 {

    }

    final class SunObj3 extends BaseObj1 {

    }
}

核心类库

  • 特定于上下文的反序列化过滤器
  • 增强的伪随机数生成器
/**
 *增强的伪随机数生成器 Enhanced Pseudo-Random Number Generators
 */
public class RandomMain {

/*
    public static void main(String[] args) {
        ThreadLocalRandom randomGenerator = ThreadLocalRandom.current();
        System.out.println(randomGenerator.nextInt(10));
    }
*/
    //基础用法
    public static void main(String[] args) {

        //基础用法
        RandomGeneratorFactory<RandomGenerator> l128X256MixRandom = RandomGeneratorFactory.of("L128X256MixRandom");
        // 使用时间戳作为随机数种子
        RandomGenerator randomGenerator = l128X256MixRandom.create(System.currentTimeMillis());
        for (int i = 0; i < 5; i++) {
            System.out.println(randomGenerator.nextInt(10));
        }
        //你也可以遍历出所有的 PRNG 算法。
        RandomGeneratorFactory.all().forEach(factory -> {
            System.out.println(factory.group() + ":" + factory.name());
        });

        //System.out.println:
        //LXM:L32X64MixRandom
        //LXM:L128X128MixRandom
        //LXM:L64X128MixRandom
        //Legacy:SecureRandom
        //LXM:L128X1024MixRandom
        //LXM:L64X128StarStarRandom
        //Xoshiro:Xoshiro256PlusPlus
        //LXM:L64X256MixRandom
        //Legacy:Random
        //Xoroshiro:Xoroshiro128PlusPlus
        //LXM:L128X256MixRandom
        //Legacy:SplittableRandom
        //LXM:L64X1024MixRandom

        /**
         * 可以看到 Legacy:Random 也在其中,新的 API 兼容了老的 Random 方式,
         * 所以你也可以使用新的 API 调用 Random 类生成随机数。
         */
        // 使用 Random
        RandomGeneratorFactory<RandomGenerator> Random = RandomGeneratorFactory.of("Random");
        // 使用时间戳作为随机数种子
        RandomGenerator random = Random.create(System.currentTimeMillis());
        for (int i = 0; i < 5; i++) {
            System.out.println(random.nextInt(10));
        }


    }


}

其他改变

  • 更强封装 JDK 内部
  • 恢复始终严格的浮点语义
 	/**
     *恢复始终严格的浮点语义
     */
    public static void main(String[] args) {
        testStrictfp();
    }

    public  strictfp   static void testStrictfp() {
        System.out.println(255555555f - 255555554f);
        System.out.println(25555555f - 25555554f);
        System.out.println(1.7 - 1.6);
        double a = 0.05;
        double b = 0.01;
        double sum =a+b;
        System.out.println(sum);
        //这样的结果如果是在银行或者其他商业计算中出现那么结果是不能想像的。所以有严格的浮点计算。JAVA对这样的计算是封装在BigDecimal类中的。
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~");
        //官方的例子:
        double d = 8e+307;
        System.out.println(4.0 * d * 0.5);
        System.out.println(2.0 * d);
        //Double.MAX_VALUE大概是1.8e+308
        /**
         *
         * 扩展指数范围
         * 体会一下,也就是在某种情况下,数字的范围不一样。并不限定于32位float和64位double了。上面的计算结果,可能就不是Infinity,而是1.6E308或其他值。
         * 一旦使用了strictfp来声明一个类、接口或者方法时,那么所声明的范围内Java的编译器以及运行环境会完全依照浮点规范IEEE-754来执行。
         *
         * IEEE-754到底是个什么东东呢?
         * IEEE二进制浮点数算术标准(IEEE 754)是最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。  http://zh.wikipedia.org/wiki/IEEE_754\
         * 大致意思是:在一个FP-strict表达式中间的所有值必须是float或者double,这意味着运算结果也肯定是IEEE754中所规定的单精度和双精度数,也即是32位的float和64位的double。
         * 这里也只说明了,float和double的规定,是符合IEEE754标准的。
         *
         * 1.strictfp翻译为“精确的浮点”不够贴切,容易引起误解。
         *
         * 2.strictfp关键字的使用与IEEE754没有直接因果关系。IEEE 754,是IEEE制定的,而不是J2EE标准:)
         *
         * 3.使用strictfp关键字的目的,是保证平台移植之后,浮点运算结果是一致的。
         */
    }

JVM重要特性详解

GC变化

  • JDK 9:设置 G1 为 JVM 默认垃圾收集器
  • JDK 10:并行全垃圾回收器 G1,通过并行 Full GC 改善 G1 的延迟。目前对 G1 的 full GC 的实现采用了单线程-清除-压缩算法。JDK 10 开始使用并行化-清除-压缩算法。
  • JDK 11:推出 ZGC 新一代垃圾回收器(实验性),目标是GC暂停时间不会超过 10 毫秒,既能处理几百兆的小堆,也能处理几个 T 的大堆。
  • JDK 14 :删除 CMS 垃圾回收器;弃用 ParallelScavenge + SerialOld GC 的垃圾回收算法组合;将 ZGC 垃圾回收器移植到 macOS 和 Windows 平台
  • JDk 15 : ZGC( JEP 377)和 Shenandoah( JEP 379)不再是实验性功能。默认的 GC 仍然是 G1。
  • JDK 16:增强 ZGC,ZGC 获得了 46 个增强功能和 25 个错误修复,控制 stw 时间不超过 10 毫秒

下面我们看看使用 16GB 堆内存和 SPECjbb® 20151 基准测试对前三个收集器的测试结果。

吞吐量测试结果

吞吐量测试结果
在吞吐量方面 (数值越高表示性能越好

Parallel 中 JDK 8 和 JDK 11 差距不大,JDK 17 相较 JDK 8 提升 15% 左右;

G1 中 JDK 17 比 JDK 8 提升 18%;

ZGC 在 JDK 11引入,JDK 17 对比 JDK 11 提升超过 20%。

延迟测试结果

延迟测试结果
延迟比较

在 GC 延迟方面,JDK 17 的提升更为明显。我们可以看到为缩短 GC 暂停时间所做的努力都得到了回报,很多提升都是因为 GC 的改进。

在 Parallel 中 JDK 17 对比 JDK 8 和JDK 11 提升 40%;

在 G1 中,JDK 11 对比 JDK 8 提升 26%,JDK 17 对比 JDK 8 提升接近 60%! G1最佳提升~~~

ZGC 中 JDK 17 对比 JDK 11 提升超过 40%。

暂停时间测试结果

暂停时间测试结果暂停时间对比

我们可以看到 JDK 17 中的 ZGC 远低于目标:亚毫秒级的暂停时间。

G1 的目标是在延迟和吞吐量之间保持平衡,远低于其默认的目标:200 毫秒的暂停时间。

ZGC 的设计会保证暂停时间不随堆的大小而改变,我们可以清楚地看到当堆扩大到 128 GB 时的情况。

从暂停时间的角度来看,G1 比 Parallel 更善于处理更大的堆,因为它能够保证暂停时间满足特定目标。

资源占用测试结果

资源占用测试结果
资源占用

上图比较了三个不同收集器原生内存的使用峰值。由于从这个角度来看 Parallel 和 ZGC 都非常稳定,因此我们应该看一看原始数字。

我们可以看到 G1 在这方面确实有所改进,主要原因是所有功能和增强功能都提高了记忆集管理的效率 。

GC总结

  • 无论使用哪种收集器,与旧版本相比,JDK 17 的整体性能都有很大的提升。

  • 在 JDK 8 中,Parallel 是默认设置,但在 JDK 9 中改为了 G1。从那以后,G1 的改进速度就超过了 Parallel,但在有些情况下可能 Parallel 仍然是最佳选择。而 ZGC(JDK 15 正式使用)的加入,成为了第三种高性能替代方案。

升级必要性

  • Spring 带头猛冲,直接上 JDK 17。 如果 Spring 6 还支持 Java 8 的话,那很多技术框架都要跟着 Java 8 的兼容,与其这样不如由 Spring 带头,一起飞升 Java 17,不过有些框架还不支持 JDK 17。
  • 性能升级,光从 Java 8 换到 Java 11,啥也没干性能直接就提升了 10%(NIO 底层的重写),更何况一路到 JDK 17 过程中的 JVM 相关优化。不过光是性能的优化还不足以吸引企业进行JDK升级,毕竟加机器就能解决,费不着各种升级改造, 还可能有安全问题。
  • JDK 21 可能成为真正的经典版本。 目前还没有 Project loom 功能,代表着没有协程,性能方面比有协程 JDK 差远了。比如阿里开源的 JDK 8、11 就有非侵入式协程。Project loom 功能在 JDK 19 已经可预览了,Project loom 大概在 JDK 21 时正式推出,而JDK21 又是一个 LTS 版本 ,值得期待。

学习目的

为了升级JDK

  • 升级jdk是必然趋势,8-17的升级跨度太大,我们需要知道到底变化了哪些,有哪些优化是我们可以采用的。

感受jdk研发者的思路 感受java语言的进步

阅读源码

  • 现在开源框架的底层最低jdk版本越来越高,采用的语法糖越来越新,如果不学习新的jdk,那么对于我们阅读源码也越来越吃力。

新语法的好处

  • java语言常备吐槽的一点,就是语言臃肿。新的语法支持可以让我们轻便开发。随着jdk功能的强大我们的开发效率以及系统功能的稳定性都有所提升。简单的说 一个系统jdk的升级 就等于做了一次大的优化。

保持新鲜

  • 在升级后面临着大量的语法糖,当同事写了一段很简练的代码完成了功能后,对其语义无法理解。程序员是要保持新鲜的。

再好的总结都值得你再去看一遍 开发者文档 ~~~

资料

笔者整理的思维笔记:ProcessOn
新特性相关代码实例
Java各个版本的文档入口:https://docs.oracle.com/en/java/javase/index.html
Java各个版本下载:https://jdk.java.net/archive/

参考文献

https://baijiahao.baidu.com/s?id=1749917039132965540&wfr=spider&for=pc

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 背景 1.1 企业初涉电子商务面临的困扰 1.1.1 不知从何入手 一般企业的IT水平大多处于基本应用阶段,如打字(Word)、统计(Excel)、交流(发邮件、QQ等)、上网(浏览网页、搜索)等等,要上电子商务时,门在哪里是个现实的问题 1.1.2 没有配套的管理系统 需要专门的进销存及库管系统 1.1.3 成本太高 建设成本高、配套系统成本高、专业团队成本高 1.1.4 回报无期 巨大的投入以后,能不能卖得好又是一个现实的问题,卖得好才能确保企业的预期的回报,虽然前边的每一顶都已经做得很到位,但是卖得好不好,谁知道呢? 1.2 轻开商贸企业基础级电子商务B2C网站公共版的“零”成本解决方案,您只需: 下载轻开电子商务系统 购买一台云主机或支持java+mysql的虚拟主机空间 发布轻开电子商务系统 设置管理一下相关后台(让系统变成您自己的东西) 发布商品和信息 同时开通一个网店(淘宝、京东或拍拍等) 恭喜恭喜,您的企业已经进入电子商务时代。 1.3 成本对比 略 2 功能模块 2.1 商品管理 商品发布:商品名称、商品分类、计量单位、价格、款(货)号、适合人群、款式、品牌、个性特点、适合季节、尺寸规格、色彩、销售地址及主图和详情等 商品修改:同上 商品删除:删除商品记录及相关图片 2.2 分类管理 商品分类:添加、修改、删除商品分类及其子分类,调整分类展示的顺序 信息菜单:添加、修改、删除菜单及其子菜单 论坛栏目:添加、修改、删除栏目及其子栏目 2.3 网站图片管理 换动态图片:主题图片管理、今日特价、主页第1张大幅广告、主页第2张大幅广告、特价广告 换图片(1张):Logo、400电话图、热卖推荐、主页第2张导航小图、主页第3张导航小图、主页第4张导航小图、主页第5张导航小图、主页第6张导航小图、主页第7张导航小图、页脚导航图 2.4 组织机构 用树状结构立体管理组织机构,明确清晰地确定部门之间的上下级关系, 2.5 职务角色管理 机动灵活地设置各种职务角色,配合组织机构树纵深管理相互间的关系 2.6 用户管理 用户与部门、职务有机结合,明确体现用户在系统中的作用和地位。 2.7 权限分配 建立功能模块识别节点号,根据部门、职务、用户综合受权,明确不同单元的操作范围和界限,彻底解决系统内应用层的安全及权限问题 2.8 修改网站名 很简单,跟前边的网站图片管理相结合,目的就是让本系统彻底成为您个人或公司的私有系统而不用花一分钱 2.9 辅助导航信息管理 注册新用户:登录即注册,让用户轻松完成 积分方式、如何购买、定购流程、配送方式、退换货政策、常见热点问题、投诉与建议、公司简介、诚聘英才、客户服务等均可更新成您的信息 3 硬件需求 3.1 基本配置 配件 数量 CPU 2核 内存 4G 硬盘 100G 3.2 最低配置 配件 数量 CPU 2核 内存 2G 硬盘 10G 4 软件需求 Windows环境 名称 分类 版本 Windows 操作系统 2003及以上 jdk Java虚拟机 1.5.x及以上 Tomcat Web服务器 5.x及以上 Easy do it (轻松互联网开发平台,简称轻开平台,原名WebEasy) 应用开发服务器 2014及以上 Mysql 数据库 5.0.x及以上 EditPlus 开发工具 2.x及以上 Linux环境 名称 分类 版本 CentOS 操作系统 5.x及以上 jdk Java虚拟机 1.5.x及以上 Tomcat Web服务器 5.x及以上 Easy do it (轻松互联网开发平台,简称轻开平台,原名WebEasy) 应用开发服务器 2014及以上 Mysql 数据库 5.0.x及以上 EditPlus 开发工具 2.x及以上 5 下载包清单 资源 分类 说明 baby 目录及文件 B2C电子商务系统及轻开源码 htok_baby.sql 数据库备份文件 系统数据结构及初始数据 轻开B2C电子商务系统发布手册.docx 文档 发布及管理系统的技术文档 WebEasy开发手册.doc 文档 扩展开发的技术手册 20110403.zip 例子 商品图片批量上传时的格式例子 EditPlus.exe 开发工具 扩展开发本系统的最常用工具
1. 思考一下   学习Java之前,先别急,静下心来好好想想:   1)你对学习Java是否有兴趣?   2)你是否能静下心来坚持不懈地学习?   嗯,这是个容易但又绝对不应该忽略的问题,你确信自己对Java感兴趣、而且又有吃苦的准备,那你才可能学好Java!如果具备这两点条件,就请继续往下看……   2. 准备一下   请不要把你的学习Java之路和其它计算机技术分开看待,技术的联系往往是千丝万缕的,你应该掌握一些学习Java所涉及的基础知识,对于“CLASSPATH要怎么设置啊”、“跪求JDK下载地址”等等问题,你不该问,因为Internet上太多答案了,甚至换个角度说,你是不是还不适合直接学习编程?   1)买本Java学习用书。   JDK 6正式版刚刚推出,市面上你暂时买不到JDK6的书籍,但我仍推荐你买一本适合入门的、较新JDK版本Java书籍,那么,你现在选择一本讲述JDK5的入门书籍还是有条件的。我可以推荐一些入门书,但是我不打算这么做,因为合我口味的,不一定适合你。但无论如何,《Thinking In Java》绝对不应该作为你入门Java的第一本书籍!   记住,网络上学习Java的资源很多,但基本不够系统,拥有一本Java入门书籍,是你系统学习Java的基础。   2)准备Java学习环境。   准备你的学习环境,很简单,安装JDK6,然后用类似UltraEdit、EditPlus的文本编辑器配置你的学习环境,Internet上有很多关于如何配置的文章。初学Java,不建议用IDE工具,通过一行行的敲代码,你会碰到很多问题,只有这样,你才能学会怎样解决问题,并加深自己对Java的理解。   准备好后,开始进入激动人心的Java学习里程吧!   3. Java基础学习之路   学习Java的过程虽然辛苦些,但又是处处有精彩!学习过程中的心态一定要保持专一,网上关于语言间的“PK”到处都是,别被浮躁影响!认准了Java,你就坚持!克服心魔,恒心最终会给你回报的。   Java的体系分为Java SE、Java EE和Java ME(JDK 5.0以前分别叫J2SE、J2EE和J2ME),Java的敲门砖就是Java SE了,也就是你要入门学习的内容,不论你日后是选择哪个体系领域作为发展方向,你都得从Java SE起步。   学习Java SE,打好Java基础;如果想学Java EE(对不起,我不了解JavaME,所以我无法涉及JavaME的相关内容),对于AWT、Swing是否要学习呢,我个人是觉得还是要知道其所以然的,特别是其事件处理模式,我强烈建议初学者一定要弄清楚,其他具体的开发就不一定要全面掌握了,毕竟AWT、Swing方面的内容足够用几本大部头的书才能描述详尽;当然,如果你的工作就是做AWT、Swing开发,那就另当别论了。   I/O、Thread都是基础之一。   4. Java EE学习之路   学习了Java SE,还只是完成“万里长征”的第一步。   接下来选择学习Java EE或是Java ME(或者你还想不断深入学习JavaSE,只要你喜欢,你可以一直深入下去,就像前面所说的,我不会讲到Java ME的学习);估计选择Java EE方面的学习者会多一些,JavaEE所涉及技术非常多,考虑到初学者的接受程度,我会尽量从最常用的角度来介绍。   学习Java EE,在开发环境上不建议再用文本编辑器,我感觉NetBeans 5.5(目前最高正式版本)很适合初学者,我个人也很喜欢NetBeans,如果你喜欢用别的IDE如Eclipse,都没问题,看自己喜欢吧。   4.1 学习JSP/Servlet   Java EE最高版本目前是5.0版本。   在JavaEE中,首先要学习JSP/Servlet(结合Tomcat、MySQL)技术,建议JSP1.2/2.X的知识都要掌握,毕竟现在很多的在用系统都还是基于JSP1.2,学习JSP,还必须掌握一些外延技术,如:你还得掌握HTML基础知识,最好再掌握些JavaScript(目前正火的AJAX技术之一)、CSS的技术,而了解XML是必不可少的(至少描述性配置信息是通过XML来描述的)。在学习JSP/Servlet过程中,应该结合学习JDBC、数据库开发技术,毕竟绝大多数Java Web项目都是和数据库紧密结合的。   4.2 学习数据库开发技术   这里我想重点说一下数据库开发技术,数据库技术是做业务系统必备技能,JavaWeb开发人员最低程度都应该掌握SQL语句的使用!数据库技术大体可分为DBA技术和数据库开发技术,对于开发而言,应侧重数据库开发技术,而数据库基本操作,也是要掌握的。常用数据库有DB2、Oracle、SQLServer、My
JVM参数设置详细说明、JVM 参数设置详细说明 1: heap size a: -Xmx 指定jvm的最大heap大小,如:-Xmx=2g b: -Xms 指定jvm的最小heap大小,如:-Xms=2g,高并发应用,建议和-Xmx一样,防止因为内存收缩/突然增大带来的性能影响。 c: -Xmn 指定jvm中New Generation的大小,如:-Xmn256m。这个参数很影响性能,如果你的程序需要比较多的临时内存,建议设置到512M,如果用的少,尽量降低这个数值,一般来说128/256足以使用了。 d: -XX:PermSize= 指定jvm中Perm Generation的最小值,如:-XX:PermSize=32m。这个参数需要看你的实际情况,可以通过jmap命令看看到底需要多少。 e: -XX:MaxPermSize= 指定Perm Generation的最大值,如:-XX:MaxPermSize=64m f: -Xss 指定线程桟大小,如:-Xss128k,一般来说,webx框架下的应用需要256K。如果你的程序有大规模的递归行为,请考虑设置到512K/1M。这个需要全面的测试才能知道。不过,256K已经很大了。这个参数对性能的影响比较大的。 g: -XX:NewRatio= 指定jvm中Old Generation heap size与New Generation的比例,在使用CMS GC的情况下此参数失效,如:-XX:NewRatio=2 h: -XX:SurvivorRatio= 指定New Generation中Eden Space与一个Survivor Space的heap size比例,-XX:SurvivorRatio=8,那么在总共New Generation为10m的情况下,Eden Space为8m i: -XX:MinHeapFreeRatio= 指定jvm heap在使用率小于n的情况下,heap进行收缩,Xmx==Xms的情况下无效,如:-XX:MinHeapFreeRatio=30 j: -XX:MaxHeapFreeRatio= 指定jvm heap在使用率大于n的情况下,heap 进行扩张,Xmx==Xms的情况下无效,如:-XX:MaxHeapFreeRatio=70 k: -XX:LargePageSizeInBytes= 指定Java heap的分页页面大小, 如:-XX:LargePageSizeInBytes=128m 2: garbage collector a: -XX:+UseParallelGC 指定在New Generation使用parallel collector,并行收集,暂停,app threads,同时启动多个垃圾回收thread,不能和CMS gc一起使用。系统吨吐量优先,但是会有较长长时间的app pause,后台系统任务可以使用此 gc b: -XX:ParallelGCThreads= 指定parallel collection时启动的thread个数,默认是物理processor的个数 c: -XX:+UseParallelOldGC 指定在Old Generation使用parallel collector d: -XX:+UseParNewGC 指定在New Generation使用parallel collector,是UseParallelGC的gc的升级版本,有更好的性能或者优点,可以和CMS gc一起使用 e: -XX:+CMSParallelRemarkEnabled 在使用UseParNewGC的情况下,尽量减少mark的时间 f: -XX:+UseConcMarkSweepGC 指定在Old Generation使用concurrent cmark sweep gc、gc thread和app thread并行(在init-mark和remark时pause app thread)。app pause时间较短,适合交互性强的系统,如web server g: -XX:+UseCMSCompactAtFullCollection 在使用concurrent gc的情况下,防止memory fragmention,对live object进行整理,使memory 碎片减少 h: -XX:CMSInitiatingOccupancyFraction= 指示在old generation 在使用了n%的比例后,启动concurrent collector,默认值是68,如:-XX:CMSInitiatingOccupancyFraction=70 有个bug,在低版本(1.5.09 and early)的jvm上出现, http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6486089 i: -XX:+UseCMSInitiatingOccupancyOnly 指示只有在old generation在使用了初始化的比例后concurrent collector启动收集 3:others a: -XX:MaxTenuringThreshold= 指定一个object在经历了n次young gc后转移到old generation区,在linux64的java6下默认值是15,此参数对于throughput collector无效,如:-XX:MaxTenuringThreshold=31 b: -XX:+DisableExplicitGC 禁止java程序中的full gc,如System.gc()的调用。最好加上么,防止程序在代码里误用了。对性能造成冲击。 c: -XX:+UseFastAccessorMethods get、set方法转成本地代码 d: -XX:+PrintGCDetails 打应垃圾收集的情况如: [GC 15610.466: [ParNew: 229689K->20221K(235968K), 0.0194460 secs] 1159829K->953935K(2070976K), 0.0196420 secs] e: -XX:+PrintGCTimeStamps 打应垃圾收集的时间情况,如: [Times: user=0.09 sys=0.00, real=0.02 secs] f: -XX:+PrintGCApplicationStoppedTime 打应垃圾收集时,系统的停顿时间,如: Total time for which application threads were stopped: 0.0225920 seconds 4: a web server product sample and process JAVA_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 " 最初的时候我们用UseParallelGC和UseParallelOldGC,heap开了3G,NewRatio设成1。这样的配置下young gc发生频率约12、3秒一次,平均每次花费80ms左右,full gc发生的频率极低,每次消耗1s左右。从所有gc消耗系统时间看,系统使用率还是满高的,但是不论是young gc还是old gc,application thread pause的时间比较长,不合适 web 应用。我们也调小New Generation的,但是这样会使full gc时间加长。 后来我们就用CMS gc(-XX:+UseConcMarkSweepGC),当时的总heap还是3g,新生代1.5g后,观察不是很理想,改为jvm heap为2g新生代设置-Xmn1g,在这样的情况下young gc发生的频率变成7、8秒一次,平均每次时间40-50毫秒左右,CMS gc很少发生,每次时间在init-mark和remark(two steps stop all app thread)总共平均花费80-90ms左右。 在这里我们曾经New Generation调大到1400m,总共2g的jvm heap,平均每次ygc花费时间60-70ms左右,CMS gc的init-mark和remark之和平均在50ms左右,这里我们意识到错误的方向,或者说CMS的作用,所以进行了修改。 最后我们调小New Generation为256m,young gc 2、3秒发生一次,平均停顿时间在25毫秒左右,CMS gc的init-mark和remark之和平均在50ms左右,这样使系统比较平滑,经压力测试,这个配置下系统性能是比较高的。 在使用CMS gc的时候他有两种触发gc的方式:gc估算触发和heap占用触发。我们的1.5.0.09 环境下有次old 区heap占用在30%左右,她就频繁gc,个人感觉系统估算触发这种方式不靠谱,还是用 heap 使用比率触发比较稳妥。 这些数据都来自64位测试机,过程中的数据都是我在jboss log找的,当时没有记下来,可能存在一点点偏差,但不会很大,基本过程就是这样。 5: 总结 web server作为交互性要求较高的应用,我们应该使用Parallel+CMS,UseParNewGC这个在jdk6 -server上是默认的new generation gc,新生代不能太大,这样每次pause会短一些。CMS mark-sweep generation可以大一些,可以根据pause time实际情况控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值