分类 技术 下的文章

了解如何在 Arch Linux 上安装 Docker,并使用 Docker Compose 和制表符补全为运行容器做好准备。

在 Arch Linux 上安装 Docker 很简单。它可以在 Extra 仓库中找到,你可以简单地 执行 pacman 魔法

sudo pacman -S docker

但要在 Arch Linux 上正确运行 Docker,还需要执行更多步骤。

让 Arch Docker 做好准备

这一切都归结为以下步骤:

  • 从 Arch 仓库安装 Docker
  • 启动 Docker 守护进程并在每次启动时自动运行
  • 将用户添加到 docker 组以运行 docker 命令而无需 sudo

让我们看看详细步骤。

步骤 1:安装 Docker 包

打开终端并使用以下命令:

sudo pacman -S docker

输入密码并在询问时按 Y

这可能需要一些时间,具体取决于你使用的镜像。

? 如果你看到找不到包或 404 错误,那么你的同步数据库可能是旧的。使用以下命令更新系统(它将下载大量软件包并需要时间): sudo pacman -Syu

步骤 2:启动 docker 守护进程

Docker 已安装但未运行。你应该在第一次运行 Docker 命令之前启动 Docker 守护进程:

sudo systemctl start docker.service

我还建议启用 Docker 服务,以便 Docker 守护进程在系统启动时自动启动。

sudo systemctl enable docker.service

这样,你就可以开始运行 docker 命令了。你不再需要手动启动 Docker 服务。

步骤 3:将用户添加到 docker 组

Docker 已安装并且 Docker 服务正在运行。你几乎已准备好运行 docker 命令。

但是,默认情况下,你需要将 sudodocker 命令一起使用。这很烦人。

为了避免在每个 docker 命令中使用 sudo,你可以将自己(或任何其他用户)添加到 docker 组,如下所示:

sudo usermod -aG docker $USER

你必须注销(或关闭终端)并重新登录才能使上述更改生效。如果你不想这样做,请使用以下命令:

newgrp docker

现在已经准备好了。我们来测试一下。

步骤 4:验证 docker 安装

Docker 本身提供了一个很小的 Docker 镜像来测试 Docker 安装。运行它并查看是否一切正常:

docker run hello-world

你应该看到类似这样的输出,表明 Docker 成功运行:

恭喜! 你已经在 Arch Linux 上成功安装了 Docker。

可选:安装 Docker Compose

Docker Compose 已经成为 Docker 不可或缺的一部分。它允许你管理多个容器应用。

较早的经典 Compose 由 docker-compose Python 软件包提供。Docker 还将其移植到 Go 中,并通过 docker compose 提供,但该软件包附带 Docker Desktop

在这个阶段,我建议使用经典的 docker-compose 插件并使用以下命令安装它:

sudo pacman -S docker-compose

故障排除技巧

以下是你可能遇到的一些常见问题以及可能的解决方案:

制表符补全不适用于 docker 子命令

如果你想对 docker 命令选项使用制表符补全(例如将 im 补全到 images 等),请安装 bash-completion 包:

sudo pacman -S bash-completion

关闭终端并启动一个新终端。你现在应该能够通过 docker 命令使用制表符补全功能。

无法连接到 Docker 守护进程错误

如果你看到以下错误:

docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?.
See 'docker run --help'.

那是因为 Docker 守护进程没有运行。参考步骤 2,启动 Docker 服务,确保其正在运行并启用它,以便 Docker 守护进程在每次启动时自动运行。

sudo systemctl start docker.service
sudo systemctl enable docker.service

尝试连接到 Docker 守护程序套接字时权限被拒绝

如果你看到此错误:

ddocker: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/containers/create": dial unix /var/run/docker.sock: connect: permission denied.
See 'docker run --help'.

这是因为你需要使用 sudo 运行 docker 命令,或者将用户添加到 docker 组以在不使用 sudo 的情况下运行 docker 命令。

我希望这篇简短的文章可以帮助你在 Arch Linux 上运行 Docker。

(题图:MJ/9951f8bf-d2e5-4335-bd86-ebf89cba654d)


via: https://itsfoss.com/install-docker-arch-linux/

作者:Abhishek Prakash 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

作为领先的开源桌面操作系统,Fedora Workstation 由全球社区共同携手打造。本文将向你展示最新版本 Fedora Workstation 39 主要的用户所能见到的变化。如今,你可以在 Fedora Workstation 官方网页 下载体验,或是通过在“软件”应用内或使用 dnf system-upgrade 在你喜爱的终端模拟器进行升级。

GNOME 45

Fedora Workstation 39 搭载了 GNOME 的最新版本——GNOME 45。这个版本为几个核心应用带来了时尚的新插件、全新的图像查看器应用、新增了在特定系统上的键盘背光设置等。同时,我们的一个更具信息性的活动按钮,及多项性能改进,将伴随全面优化的整套用户体验而来。你可以在 GNOME 45 发布说明 查看更多详细信息。

内在改进

GNOME 45 在用户体验的方方面面都有所细化,以下几点只是其中的一部分:

  • 使用动态工作空间指示器取代了静态的活动按钮。新的指示器不仅显示工作空间数量,而且还能快速展示你正在关注的工作空间。

新的动态工作空间指示器

  • 新增了一个摄像头活动指示器,要启用它,你可以通过 Pipewire 访问你的摄像头。这将与麦克风、屏幕录制和屏幕录像指示器协同工作。
  • 快速设置菜单新增了一个键盘背光设置,这项设置仅支持特定的硬件设备。

新的键盘背光设置

  • 重新设计了默认的光标,并修复了原来设置中很多长期存在的问题。
  • Fedora Workstation 39 已不再支持 Adwaita-qt 与 QGnomePlatform Qt 主题,取而代之的是 Qt 应用的上游默认主题。

核心应用

在 GNOME 45,许多应用现在使用了 libadwaita 1.4 特色的新用户界面插件。这提供了美观的双色设计并将侧边栏延伸至窗口全高。这不仅使应用看起来更酷,而且在小窗口尺寸下更易于使用。可在 此处 查看详细信息。此外,新的 headerbar 插件使顶栏与窗口内容的视觉分隔更加明显。

文件、设置和日历显示新的侧边栏插件

Fedora Workstation 39 引入了 GNOME 的新图像查看器应用,内部代号为 Loupe。该应用自底而上,使用 Rust、GTK 4,和 libadwaita 进行构建以实现高性能和高度适应性。

新的图像查看器应用(截图素材来自公有领域)

在核心应用中我们还有很多细微的改进。例如:

  • “设置”应用新增了“系统详情”区块,新的键盘布局查看器,简洁的描述字段,以及优化的键盘导航功能。

新的系统详情窗口

  • 在“文件”应用中,我们对搜索结果进行了顺序优化。
  • 我们在“软件”应用中提供了卸载 Flatpak 时删除用户数据的选项。

卸载 Flatpak 应用时显示的新提示

  • “日历”应用新增了逐行滚动和更有用的搜索结果功能。
  • 在“连接”应用中使用 RDP 连接时新增了复制文件、图像、文本的功能。

性能改进

在 GNOME 45,我们对性能提升投入了很多努力。

  • 现在默认优先使用硬件加速的视频解码(在支持的环境下)。
  • “文件”应用中的缩略图生成现在支持多线程处理。
  • 我们显著减少了光标的卡顿和延迟。
  • 在 GNOME Shell 中,以及“文件”、“软件”、“字符”等应用的搜索性能得到显著提升。

我们在整个技术栈中也进行了一些性能改进,包括 GLib、GTK 的 OpenGL 渲染器,和 systemd。这些性能优化工作都得益于我们在 Fedora Workstation 之前的版本中启用的帧指针!

Fedora Linux 39 的底层突破

Fedora Linux 39 也有许多值得注意的底层变化,此处列出一些:

  • Fedora Linux 39 现在将彩色 Bash 提示符作为默认设置!

彩色 Bash 提示符

  • 对于使用 Indic 脚本的语系,我们现在把 Noto 字体作为默认字体,取代了旧的 Lohit 字体集。
  • 由于使用量较低且缺乏主动维护,模块化 仓库在 Fedora Linux 39 中不再被提供。
+ Fedora 模块构建服务将在 Fedora Linux 38 的生命周期结束后,也就是在 2024 年的 5 月左右结束。

(题图:MJ/f06d0e9b-4c2e-4947-8333-e51340a45324)


via: https://fedoramagazine.org/whats-new-fedora-workstation-39/

作者:Merlin Cooper 选题:lujun9972 译者:ChatGPT 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在本篇 Vim 快速技巧中,你将学习到剪切和复制粘贴的相关知识。

剪切、复制和粘贴文本是文本编辑中最基本的任务之一,我们都知道 Vim 有不同的处理方式。

这意味着,在你掌握它之前,你会害怕它,一旦你掌握了它,它就只是一个兔子洞。

虽然我将详细介绍剪切、复制和粘贴,但这里是本教程的基本摘要,以帮助你开始使用:

按键描述
yiw复制当前单词。
yy复制整行。
diw剪切当前单词。
dd剪掉整行。
p粘贴文本。

别担心,Vim 为你提供的选项比我上面提到的要多得多。

在本教程中,我将引导你完成以下内容:

  • 如何在 Vim 中复制文本
  • 如何在 Vim 中剪切文本
  • 如何在 Vim 中粘贴文本
  • 如何使用可视模式在 Vim 中剪切和复制文本

那么让我们从第一个开始。

如何在 Vim 编辑器中复制文本

虽然我们使用术语“复制”,但 Vim 有一个不同的术语,称为 “ 扽出 yank ”,因此从现在开始,我将使用“扽出”而不是“复制”。

正如我之前提到的,你可以使用多种方法在 Vim 中扽出文本,以下是一些有用的方法:

命令描述
nyynY扽出(复制)当前行和接下来的 n-1 行。例如,3yy 复制当前行及其下面的两行。
yaw扽出(复制)光标所在的当前单词。
yyY扽出(复制)整个当前行。
y$扽出(复制)从光标到行尾的文本。
y^y0扽出(复制)从光标到行首的文本。

要在 Vim 中扽出,请执行以下 3 个简单步骤:

  1. Esc 键切换到正常模式
  2. 移动到要复制的行或单词
  3. 按上表中的相关命令,你的文本将被复制

想学习交互式复制行的方式吗? 跳到本教程的最后一部分。

如何在 Vim 编辑器中剪切文本

在 Vim 中,你没有任何删除文本的选项。取而代之的是剪切文本,因此删除和剪切文本与 Vim 中的操作类似。

要在 Vim 中剪切文本,请按 d 命令。但你永远不会在没有任何选项的情况下使用 d 命令。你总是会添加一些东西来做更多操作。

因此,你可以使用以下一些实用方法使用 d 命令剪切文本:

命令描述
dd剪切整个当前行。
d$将文本从光标剪切到行尾。
d^d0将文本从光标剪切到行首。
ndddN剪切当前行和接下来的 n-1 行。例如,3dd 剪切当前行及其下面的两行。
daw剪切光标所在的当前单词。

假设我想从文件中剪切前 4 行,然后我需要使用 4dd,我是这样做的:

如何在 Vim 编辑器中粘贴文本

在 Vim 中复制或剪切文本后,只需按 p 键即可粘贴它。

你可以多次按 p 键多次粘贴文本,也可以使用 np,其中 n 是要粘贴文本的次数。

例如,在这里,我粘贴了之前复制了三遍的行:

就是这么简单!

如何通过选择文本来剪切和复制文本

如果你使用过 GUI 文本编辑器,那么你肯定习惯于通过选择文本来复制和剪切文本。

让我们从如何通过在 Vim 中选择文本来复制开始。

通过选择文本复制

要在可视模式下复制文本,请执行以下 3 个简单步骤:

  1. 移动到要开始选择的地方
  2. Ctrl + v 启用可视模式
  3. 使用箭头键进行选择
  4. y 键复制所选文本

例如,在这里,我使用可视模式复制了 4 行:

如果你注意到,当我按下 y 键,它就会显示有多少行被扽出(复制)。就我而言,有 4 行被复制。

在 Vim 中选择文本来剪切文本

要在 Vim 中以可视模式剪切文本,你所要做的就是遵循 4 个简单步骤:

  1. 移动到要剪切的位置
  2. Ctrl + v 切换到可视模式
  3. 使用箭头键选择要剪切的行
  4. d 键剪切选定的行

假设我想剪掉 4 行,那么我会这样做:

挺容易。是吧?

更多关于 Vim 的内容

你知道 Vim 有多种模式吗? 了解有关 Vim 中不同模式的更多信息

想提高你的 Vim 水平吗?请参阅 成为 Vim 专业用户的提示和技巧

我希望本指南对你有所帮助。

(题图:MJ/3555eed2-ab14-433d-920f-17b80b46ce74)


via: https://itsfoss.com/vim-cut-copy-paste/

作者:Sagar Sharma 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

通过理解解释、即时编译和预先编译之间的区别,有效地使用它们。

Java 是一种跨平台的编程语言。程序源代码会被编译为 字节码 bytecode ,然后字节码在运行时被转换为 机器码 machine code 解释器 interpreter 在物理机器上模拟出的抽象计算机上执行字节码指令。 即时 just-in-time (JIT)编译发生在运行期,而 预先 ahead-of-time (AOT)编译发生在构建期。

本文将说明解释器、JIT 和 AOT 分别何时起作用,以及如何在 JIT 和 AOT 之间权衡。

源代码、字节码、机器码

应用程序通常是由 C、C++ 或 Java 等编程语言编写。用这些高级编程语言编写的指令集合称为源代码。源代码是人类可读的。要在目标机器上执行它,需要将源代码转换为机器可读的机器码。这个转换工作通常是由 编译器 compiler 来完成的。

然而,在 Java 中,源代码首先被转换为一种中间形式,称为字节码。字节码是平台无关的,所以 Java 被称为平台无关编程语言。Java 编译器 javac 将源代码转换为字节码。然后解释器解释执行字节码。

下面是一个简单的 Java 程序, Hello.java

//Hello.java
public class Hello {
    public static void main(String[] args) {
         System.out.println("Inside Hello World!");
    }
}

使用 javac 编译它,生成包含字节码的 Hello.class 文件。

$ javac Hello.java
$ ls
Hello.class  Hello.java

现在,使用 javap 来反汇编 Hello.class 文件的内容。使用 javap 时如果不指定任何选项,它将打印基本信息,包括编译这个 .class 文件的源文件、包名称、公共和受保护字段以及类的方法。

$ javap Hello.class
Compiled from "Hello.java"
public class Hello {
    public Hello();
    public static void main(java.lang.String[]);
}

要查看 .class 文件中的字节码内容,使用 -c 选项:

$ javap -c Hello.class
Compiled from "Hello.java"
public class Hello {
  public Hello();
        Code:
           0: aload_0
           1: invokespecial #1                      // Method java/lang/Object."<init>":()V
           4: return

  public static void main(java.lang.String[]);
        Code:
           0: getstatic         #2                      // Field java/lang/System.out:Ljava/io/PrintStream;
           3: ldc               #3                      // String Inside Hello World!
           5: invokevirtual #4                      // Method    
java/io/PrintStream.println:(Ljava/lang/String;)V
           8: return
}

要获取更详细的信息,使用 -v 选项:

$ javap -v Hello.class

解释器,JIT 和 AOT

解释器负责在物理机器上模拟出的抽象计算机上执行字节码指令。当使用 javac 编译源代码,然后使用 java 执行时,解释器在程序运行时运行并完成它的目标。

$ javac Hello.java
$ java Hello
Inside Hello World!

JIT 编译器也在运行期发挥作用。当解释器解释 Java 程序时,另一个称为运行时 分析器 profiler 的组件将静默地监视程序的执行,统计各部分代码被解释的次数。基于这些统计信息可以检测出程序的 热点 hotspot ,即那些经常被解释的代码。一旦代码被解释次数超过设定的阈值,它们满足被 JIT 编译器直接转换为机器码的条件。所以 JIT 编译器也被称为分析优化的编译器。从字节码到机器码的转换是在程序运行过程中进行的,因此称为即时编译。JIT 减少了解释器将同一组指令模拟为机器码的负担。

AOT 编译器在构建期编译代码。在构建时将需要频繁解释和 JIT 编译的代码直接编译为机器码可以缩短 Java 虚拟机 Java Virtual Machine (JVM) 的 预热 warm-up 时间。(LCTT 译注:Java 程序启动后首先字节码被解释执行,此时执行效率较低。等到程序运行了足够的时间后,代码热点被检测出来,JIT 开始发挥作用,程序运行效率提升。JIT 发挥作用之前的过程就是预热。)AOT 是在 Java 9 中引入的一个实验性特性。jaotc 使用 Graal 编译器(它本身也是用 Java 编写的)来实现 AOT 编译。

Hello.java 为例:

//Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("Inside Hello World!");
    }
}


$ javac Hello.java
$ jaotc --output libHello.so Hello.class
$ java -XX:+UnlockExperimentalVMOptions -XX:AOTLibrary=./libHello.so Hello
Inside Hello World!

解释和编译发生的时机

下面通过例子来展示 Java 在什么时候使用解释器,以及 JIT 和 AOT 何时参与进来。这里有一个简单的程序 Demo.java :

//Demo.java
public class Demo {
    public int square(int i) throws Exception {
        return(i*i);
    }


    public static void main(String[] args) throws Exception {
        for (int i = 1; i <= 10; i++) {
            System.out.println("call " + Integer.valueOf(i));
            long a = System.nanoTime();
            Int r = new Demo().square(i);
            System.out.println("Square(i) = " + r);
            long b = System.nanoTime();
            System.out.println("elapsed= " + (b-a));
            System.out.println("--------------------------------");
        }
    }
}

在这个程序的 main() 方法中创建了一个 Demo 对象的实例,并调用该实例的 square()方法,然后显示 for 循环迭代变量的平方值。编译并运行它:

$ javac Demo.java
$ java Demo
1 iteration
Square(i) = 1
Time taken= 8432439
--------------------------------
2 iteration
Square(i) = 4
Time taken= 54631
--------------------------------
.
.
.
--------------------------------
10 iteration
Square(i) = 100
Time taken= 66498
--------------------------------

上面的结果是由谁产生的呢?是解释器,JIT 还是 AOT?在目前的情况下,它完全是通过解释产生的。我是怎么得出这个结论的呢?只有代码被解释的次数必须超过某个阈值时,这些热点代码片段才会被加入 JIT 编译队列。只有这时,JIT 编译才会发挥作用。使用以下命令查看 JDK 11 中的该阈值:

$ java -XX:+PrintFlagsFinal -version | grep CompileThreshold
 intx CompileThreshold     = 10000                                      {pd product} {default}
[...]
openjdk version "11.0.13" 2021-10-19
OpenJDK Runtime Environment 18.9 (build 11.0.13+8)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8, mixed mode, sharing)

上面的输出表明,一段代码被解释 10,000 次才符合 JIT 编译的条件。这个阈值是否可以手动调整呢?是否有 JVM 标志可以指示出方法是否被 JIT 编译了呢?答案是肯定的,而且有多种方式可以达到这个目的。

使用 -XX:+PrintCompilation 选项可以查看一个方法是否被 JIT 编译。除此之外,使用 -Xbatch 标志可以提高输出的可读性。如果解释和 JIT 同时发生,-Xbatch 可以帮助区分两者的输出。使用这些标志如下:

$ java -Xbatch  -XX:+PrintCompilation  Demo
         34        1        b  3           java.util.concurrent.ConcurrentHashMap::tabAt (22 bytes)
         35        2         n 0           jdk.internal.misc.Unsafe::getObjectVolatile (native)   
         35        3        b  3           java.lang.Object::<init> (1 bytes)
[...]
        210  269         n 0           java.lang.reflect.Array::newArray (native)   (static)
        211  270        b  3           java.lang.String::substring (58 bytes)
[...]
--------------------------------
10 iteration
Square(i) = 100
Time taken= 50150
--------------------------------

注意,上面命令的实际输出太长了,这里我只是截取了一部分。输出很长的原因是除了 Demo 程序的代码外,JDK 内部类的函数也被编译了。由于我的重点是 Demo.java 代码,我希望排除内部包的函数来简化输出。通过选项 -XX:CompileCommandFile 可以禁用内部类的 JIT:

$ java -Xbatch -XX:+PrintCompilation -XX:CompileCommandFile=hotspot_compiler Demo

在选项 -XX:CompileCommandFile 指定的文件 hotspot_compiler 中包含了要排除的包:

$ cat hotspot_compiler
quiet
exclude java/* *
exclude jdk/* *
exclude sun/* *

第一行的 quiet 告诉 JVM 不要输出任何关于被排除类的内容。用 -XX:CompileThreshold 将 JIT 阈值设置为 5。这意味着在解释 5 次之后,就会进行 JIT 编译:

$ java -Xbatch -XX:+PrintCompilation -XX:CompileCommandFile=hotspot_compiler \
-XX:CompileThreshold=5 Demo
        47      1       n 0     java.lang.invoke.MethodHandle::linkToStatic(LLLLLL)L (native)   
           (static)
        47      2       n 0     java.lang.invoke.MethodHandle::invokeBasic(LLLLL)L (native)   
        47      3       n 0     java.lang.invoke.MethodHandle::linkToSpecial(LLLLLLL)L (native)   
           (static)
        48      4       n 0     java.lang.invoke.MethodHandle::linkToStatic(L)I (native)   (static)
        48      5       n 0     java.lang.invoke.MethodHandle::invokeBasic()I (native)   
        48      6       n 0     java.lang.invoke.MethodHandle::linkToSpecial(LL)I (native)   
           (static)
[...]
        1 iteration
        69   40         n 0     java.lang.invoke.MethodHandle::linkToStatic(ILIIL)I (native)   
           (static)
[...]
Square(i) = 1
        78   48         n 0     java.lang.invoke.MethodHandle::linkToStatic(ILIJL)I (native)   
(static)
        79   49         n 0     java.lang.invoke.MethodHandle::invokeBasic(ILIJ)I (native)   
[...]
        86   54         n 0     java.lang.invoke.MethodHandle::invokeBasic(J)L (native)   
        87   55         n 0     java.lang.invoke.MethodHandle::linkToSpecial(LJL)L (native)   
(static)
Time taken= 8962738
--------------------------------
2 iteration
Square(i) = 4
Time taken= 26759
--------------------------------

10 iteration
Square(i) = 100
Time taken= 26492
--------------------------------

好像输出结果跟只用解释时并没有什么区别。根据 Oracle 的文档,这是因为只有禁用 TieredCompilation-XX:CompileThreshold 才会生效:

$ java -Xbatch -XX:+PrintCompilation -XX:CompileCommandFile=hotspot_compiler \
-XX:-TieredCompilation -XX:CompileThreshold=5 Demo
124     1       n       java.lang.invoke.MethodHandle::linkToStatic(LLLLLL)L (native)   (static)
127     2       n       java.lang.invoke.MethodHandle::invokeBasic(LLLLL)L (native)   
[...]
1 iteration
        187   40        n       java.lang.invoke.MethodHandle::linkToStatic(ILIIL)I (native)   (static)
[...]
(native)   (static)
        212   54        n       java.lang.invoke.MethodHandle::invokeBasic(J)L (native)   
        212   55        n       java.lang.invoke.MethodHandle::linkToSpecial(LJL)L (native)   (static)
Time taken= 12337415
[...]
--------------------------------
4 iteration
Square(i) = 16
Time taken= 37183
--------------------------------
5 iteration
        214   56        b       Demo::<init> (5 bytes)
        215   57        b       Demo::square (16 bytes)
Square(i) = 25
Time taken= 983002
--------------------------------
6 iteration
Square(i) = 36
Time taken= 81589
[...]
10 iteration
Square(i) = 100
Time taken= 52393

可以看到在第五次迭代之后,代码片段被 JIT 编译了:

--------------------------------
5 iteration
        214   56        b       Demo::<init> (5 bytes)
        215   57        b       Demo::square (16 bytes)
Square(i) = 25
Time taken= 983002
--------------------------------

可以看到,与 square() 方法一起,构造方法也被 JIT 编译了。在 for 循环中调用 square() 之前要先构造 Demo 实例,所以构造方法的解释次数同样达到 JIT 编译阈值。这个例子说明了在解释发生之后何时 JIT 会介入。

要查看编译后的代码,需要使用 -XX:+PrintAssembly 标志,该标志仅在库路径中有反汇编器时才起作用。对于 OpenJDK,使用 hsdis 作为反汇编器。下载合适版本的反汇编程序库,在本例中是 hsdis-amd64.so,并将其放在 Java_HOME/lib/server 目录下。使用时还需要在 -XX:+PrintAssembly 之前增加 -XX:+UnlockDiagnosticVMOptions 选项。否则,JVM 会给你一个警告。

完整命令如下:

$ java -Xbatch -XX:+PrintCompilation -XX:CompileCommandFile=hotspot_compiler \ -XX:-TieredCompilation -XX:CompileThreshold=5 -XX:+UnlockDiagnosticVMOptions \ -XX:+PrintAssembly Demo
[...]
5 iteration
        178   56        b       Demo::<init> (5 bytes)
Compiled method (c2)    178   56                Demo::<init> (5 bytes)
 total in heap  [0x00007fd4d08dad10,0x00007fd4d08dafe0] = 720
 relocation     [0x00007fd4d08dae88,0x00007fd4d08daea0] = 24
[...]
 handler table  [0x00007fd4d08dafc8,0x00007fd4d08dafe0] = 24
[...]
 dependencies   [0x00007fd4d08db3c0,0x00007fd4d08db3c8] = 8
 handler table  [0x00007fd4d08db3c8,0x00007fd4d08db3f8] = 48
----------------------------------------------------------------------
Demo.square(I)I  [0x00007fd4d08db1c0, 0x00007fd4d08db2b8]  248 bytes
[Entry Point]
[Constants]
  # {method} {0x00007fd4b841f4b0} 'square' '(I)I' in 'Demo'
  # this:       rsi:rsi   = 'Demo'
  # parm0:      rdx     = int
  #             [sp+0x20]  (sp of caller)
[...]
[Stub Code]
  0x00007fd4d08db280: movabs $0x0,%rbx          ;   {no_reloc}
  0x00007fd4d08db28a: jmpq   0x00007fd4d08db28a  ;   {runtime_call}
  0x00007fd4d08db28f: movabs $0x0,%rbx          ;   {static_stub}
  0x00007fd4d08db299: jmpq   0x00007fd4d08db299  ;   {runtime_call}
[Exception Handler]
  0x00007fd4d08db29e: jmpq   0x00007fd4d08bb880  ;   {runtime_call ExceptionBlob}
[Deopt Handler Code]
  0x00007fd4d08db2a3: callq  0x00007fd4d08db2a8
  0x00007fd4d08db2a8: subq   $0x5,(%rsp)
  0x00007fd4d08db2ad: jmpq   0x00007fd4d08a01a0  ;   {runtime_call DeoptimizationBlob}
  0x00007fd4d08db2b2: hlt    
  0x00007fd4d08db2b3: hlt    
  0x00007fd4d08db2b4: hlt    
  0x00007fd4d08db2b5: hlt    
  0x00007fd4d08db2b6: hlt    
  0x00007fd4d08db2b7: hlt    
ImmutableOopMap{rbp=NarrowOop }pc offsets: 96
ImmutableOopMap{}pc offsets: 112
ImmutableOopMap{rbp=Oop }pc offsets: 148 Square(i) = 25
Time taken= 2567698
--------------------------------
6 iteration
Square(i) = 36
Time taken= 76752
[...]
--------------------------------
10 iteration
Square(i) = 100
Time taken= 52888

我只截取了输出中与 Demo.java 相关的部分。

现在再来看看 AOT 编译。它是在 JDK9 中引入的特性。AOT 是用于生成 .so 这样的库文件的静态编译器。用 AOT 可以将指定的类编译成 .so 库。这个库可以直接执行,而不用解释或 JIT 编译。如果 JVM 没有检测到 AOT 编译的代码,它会进行常规的解释和 JIT 编译。

使用 AOT 编译的命令如下:

$ jaotc --output=libDemo.so Demo.class

用下面的命令来查看共享库的符号表:

$ nm libDemo.so

要使用生成的 .so 库,使用 -XX:+UnlockExperimentalVMOptions-XX:AOTLibrary

$ java -XX:+UnlockExperimentalVMOptions -XX:AOTLibrary=./libDemo.so Demo
1 iteration
Square(i) = 1
Time taken= 7831139
--------------------------------
2 iteration
Square(i) = 4
Time taken= 36619
[...]
10 iteration
Square(i) = 100
Time taken= 42085

从输出上看,跟完全用解释的情况没有区别。为了确认 AOT 发挥了作用,使用 -XX:+PrintAOT

$ java -XX:+UnlockExperimentalVMOptions -XX:AOTLibrary=./libDemo.so -XX:+PrintAOT Demo
         28        1         loaded        ./libDemo.so  aot library
         80        1         aot[ 1]   Demo.main([Ljava/lang/String;)V
         80        2         aot[ 1]   Demo.square(I)I
         80        3         aot[ 1]   Demo.<init>()V
1 iteration
Square(i) = 1
Time taken= 7252921
--------------------------------
2 iteration
Square(i) = 4
Time taken= 57443
[...]
10 iteration
Square(i) = 100
Time taken= 53586

要确认没有发生 JIT 编译,用如下命令:

$ java -XX:+UnlockExperimentalVMOptions -Xbatch -XX:+PrintCompilation \ -XX:CompileCommandFile=hotspot_compiler -XX:-TieredCompilation \ -XX:CompileThreshold=3 -XX:AOTLibrary=./libDemo.so -XX:+PrintAOT Demo
         19        1         loaded        ./libDemo.so  aot library
         77        1         aot[ 1]   Demo.square(I)I
         77        2         aot[ 1]   Demo.main([Ljava/lang/String;)V
         77        3         aot[ 1]   Demo.<init>()V
         77        2         aot[ 1]   Demo.main([Ljava/lang/String;)V   made not entrant
[...]
4 iteration
Square(i) = 16
Time taken= 43366
[...]
10 iteration
Square(i) = 100
Time taken= 59554

需要特别注意的是,修改被 AOT 编译了的源代码后,一定要重新生成 .so 库文件。否则,过时的的 AOT 编译库文件不会起作用。例如,修改 square() 方法,使其计算立方值:

//Demo.java
public class Demo {

    public int square(int i) throws Exception {
        return(i*i*i);
    }

    public static void main(String[] args) throws Exception {
        for (int i = 1; i <= 10; i++) {
          System.out.println("" + Integer.valueOf(i)+" iteration");
          long start = System.nanoTime();
          int r= new Demo().square(i);
          System.out.println("Square(i) = " + r);
          long end = System.nanoTime();
          System.out.println("Time taken= " + (end-start));
          System.out.println("--------------------------------");
        }
    }
}

重新编译 Demo.java

$ java Demo.java

但不重新生成 libDemo.so。使用下面命令运行 Demo

$ java -XX:+UnlockExperimentalVMOptions -Xbatch -XX:+PrintCompilation -XX:CompileCommandFile=hotspot_compiler -XX:-TieredCompilation -XX:CompileThreshold=3 -XX:AOTLibrary=./libDemo.so -XX:+PrintAOT Demo
         20        1         loaded        ./libDemo.so  aot library
         74        1         n           java.lang.invoke.MethodHandle::linkToStatic(LLLLLL)L (native)   (static)
2 iteration
sqrt(i) = 8
Time taken= 43838
--------------------------------
3 iteration
        137   56        b            Demo::<init> (5 bytes)
        138   57        b            Demo::square (6 bytes)
sqrt(i) = 27
Time taken= 534649
--------------------------------
4 iteration
sqrt(i) = 64
Time taken= 51916
[...]
10 iteration
sqrt(i) = 1000
Time taken= 47132

可以看到,虽然旧版本的 libDemo.so 被加载了,但 JVM 检测出它已经过时了。每次生成 .class 文件时,都会在类文件中添加一个指纹,并在 AOT 库中保存该指纹。修改源代码后类指纹与旧的 AOT 库中的指纹不匹配了,所以没有执行 AOT 编译生成的原生机器码。从输出可以看出,现在实际上是 JIT 在起作用(注意 -XX:CompileThreshold 被设置为了 3)。

AOT 和 JIT 之间的权衡

如果你的目标是减少 JVM 的预热时间,请使用 AOT,这可以减少运行时负担。问题是 AOT 没有足够的数据来决定哪段代码需要预编译为原生代码。相比之下,JIT 在运行时起作用,却对预热时间有一定的影响。然而,它将有足够的分析数据来更高效地编译和反编译代码。

(题图:MJ/ed3e6e15-56c7-4c1d-aff1-84a225faeeeb)


via: https://opensource.com/article/22/8/interpret-compile-java

作者:Jayashree Huttanagoudar 选题:lkxed 译者:toknow-gh 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

想在 Ubuntu 上使用最新、最好的 LibreOffice?这里有一个简单的方法。

LibreOffice 已预装在 Ubuntu 中。

不过,如果你选择了最小化的 Ubuntu 安装,或者卸载它并安装了其他办公套件,你可以使用此命令轻松安装:

sudo apt install libreoffice

这没问题。但 Ubuntu 仓库提供的 LibreOffice 版本可能不是最新的。

如果你听说有新的 LibreOffice 版本发布,很可能你不会获得该新版本。这是因为 Ubuntu 将其保持在稳定版本上。

这对大多数用户来说都很好。但是,如果你不是“大多数用户”,并且你想在 Ubuntu 中获取最新的 LibreOffice,那么你完全可以这样做。

有两种方法可以做到这一点:

  • 使用官方 PPA(推荐)
  • 从 LibreOffice 下载 deb 文件

让我们来看看。

方法 1:通过官方 PPA 安装最新的 LibreOffice(推荐)

你可以使用官方 “LibreOffice Fresh” PPA 在基于 Ubuntu 的发行版上安装 LibreOffice 的最新稳定版本。

PPA 提供了 LibreOffice 的最新稳定版本,而不是开发版本。因此,这使其成为在 Ubuntu 上获取较新 LibreOffice 版本的理想选择。

你甚至不需要使用此方法卸载以前的版本。它将把现有的 LibreOffice 更新到新版本。

sudo add-apt-repository ppa:libreoffice/ppa
sudo apt update
sudo apt install libreoffice

由于你要添加 PPA,因此你还将获得以这种方式安装的新版本的更新。

方法 2:从网站获取二进制文件(如果需要)

你可以随时前往 LibreOfiice 网站的下载页面 下载最新版本的 deb 文件。你还会看到下载较旧但 LTS 稳定版本的选项。

我相信你已经 知道如何从 deb 文件安装应用。右键单击 deb 文件,选择使用软件中心打开它。进入软件中心后,你可以单击安装按钮进行安装。

结论

第二种方法的缺点是,如果有更新,你必须再次下载 deb 文件,删除以前的 LibreOffice 版本,然后使用新下载的 deb 文件安装新版本。

相比之下,PPA 会随着系统更新而自动更新。这就是我推荐 PPA 的原因,特别是当它由 LibreOffice 团队自己维护时。

顺便说一句,这里有一些 充分利用 LibreOffice 的技巧

提高 LibreOffice 生产力的技巧

我希望这个快速技巧可以帮助你在基于 Ubuntu 的发行版上获取最新的 LibreOffice。如果你有疑问,请告诉我。


via: https://itsfoss.com/install-libreoffice-ubuntu/

作者:Abhishek Prakash 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

开放文档格式(ODF)基于开放标准,你可以使用其它工具检查它们,甚至从中提取数据。你只需要知道从哪里开始。

过去,文字处理文件是封闭的专有格式。在一些较旧的文字处理软件中,文档文件本质上是该软件的内存转储。虽然这样可以让加载文件更快,但也使文档文件格式变得不透明。

2005 年左右, 结构化信息标准促进组织 Organization for the Advancement of Structured Information Standards (OASIS)为所有类型的办公文档定义了一种开放格式,即 办公应用程序开放文档格式 Open Document Format for Office Applications (ODF)。由于 ODF 是基于 OpenOffice.org 的 XML 文件规范的开放式标准,因此你也可以将其简称为 “开放文档格式”。ODF 包括几种文件类型,包括用于 开放文档文本OpenDocument Text 文档的 ODT。ODT 文件中有很多值得探索的内容,它的本质是一个 Zip 文件。

ODT 文件结构

跟所有 ODF 文件一样,ODT 文件实际上是一个 XML 文档和其它文件的 Zip 压缩包。使用 Zip 可以占用更少的磁盘空间,同时也意味着可以用标准 Zip 工具来检查它。

我有一篇关于 IT 领导力的文章,名为“Nibbled to death by ducks”,我将其保存为 ODT 文件。由于 ODF 文件是一个 zip 容器,你可以用 unzip 命令来检查它:

$ unzip -l 'Nibbled to death by ducks.odt'
Archive: Nibbled to death by ducks.odt
Length Date Time Name
39 07-15-2022 22:18 mimetype
12713 07-15-2022 22:18 Thumbnails/thumbnail.png
915001 07-15-2022 22:18 Pictures/10000201000004500000026DBF6636B0B9352031.png
10879 07-15-2022 22:18 content.xml
20048 07-15-2022 22:18 styles.xml
9576 07-15-2022 22:18 settings.xml
757 07-15-2022 22:18 meta.xml
260 07-15-2022 22:18 manifest.rdf
0 07-15-2022 22:18 Configurations2/accelerator/
0 07-15-2022 22:18 Configurations2/toolpanel/
0 07-15-2022 22:18 Configurations2/statusbar/
0 07-15-2022 22:18 Configurations2/progressbar/
0 07-15-2022 22:18 Configurations2/toolbar/
0 07-15-2022 22:18 Configurations2/popupmenu/
0 07-15-2022 22:18 Configurations2/floater/
0 07-15-2022 22:18 Configurations2/menubar/
1192 07-15-2022 22:18 META-INF/manifest.xml
970465 17 files

我想强调 Zip 文件结构的以下几个元素:

  1. mimetype 文件用于定义 ODF 文档。处理 ODT 文件的程序,如文字处理程序,可以使用该文件来验证文档的 MIME 类型。对于 ODT 文件,它应该总是:
application/vnd.oasis.opendocument.text
  1. META-INF 目录中有一个 manifest.xml 文件。它包含查找 ODT 文件其它组件的所有信息。任何读取 ODT 文件的程序都从这个文件开始定位其它内容。例如,我的 ODT 文档的 manifest.xml 文件包含这一行,它定义了在哪里可以找到主要内容:
<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>
  1. content.xml 文件包含文档的实际内容。
  2. 我的文档中只有一张截图,它位于 Pictures 目录中。

从 ODT 中提取文件

由于 ODT 文档是一个具有特定结构的 Zip 文件,因此可以从中提取文件。你可以先解压缩整个 ODT 文件,例如使用 unzip 命令:

$ unzip -q 'Nibbled to death by ducks.odt' -d Nibbled

一位同事最近向我要了一份我在文章中提到的图片。通过查看 META-INF/manifest.xml 文件,我找到了嵌入图像的确切位置。用 grep 命令可以找到描述图像的行:

$ cd Nibbled
$ grep image META-INF/manifest.xml
<manifest:file-entry manifest:full-path="Thumbnails/thumbnail.png" manifest:media-type="image/png"/>
<manifest:file-entry manifest:full-path="Pictures/10000201000004500000026DBF6636B0B9352031.png" manifest:media-type=" image/png”/>

我要找的图像保存在 Pictures 文件夹中。可以通过列出目录的内容来验证:

$ ls -F
Configurations2/ manifest.rdf meta.xml Pictures/ styles.xml
content.xml META-INF/ mimetype settings.xml Thumbnails/

就是这张图片:

Image of rubber ducks in two bowls

开放文档格式

ODF 是一种开放的文件格式,它可以描述文字处理文件(ODT)、电子表格文件(ODS)、演示文稿(ODP)和其它文件类型。由于 ODF 格式基于开放标准,因此可以使用其他工具检查它们,甚至从中提取数据。你只需要知道从哪里开始。所有 ODF 文件都以 META-INF/manifest.xml 为“引导”文件,通过它你能找到其余的所有内容。

(题图:MJ/d245ab34-f0b0-452c-b29a-ece9aa78f11a)


via: https://opensource.com/article/22/8/odt-files

作者:Jim Hall 选题:lkxed 译者:toknow-gh 校对:校对者ID

本文由 LCTT 原创编译,Linux中国 荣誉推出