JVM核心技术-23内存分析与相关工具上篇(内存布局与分析工具)

23 内存分析与相关工具上篇(内存布局与分析工具)

通过前面的课程,我们学习了"内存溢出"和"内存泄漏"的区别。

简单来说,Java 中的内存溢出就是内存不够用,一般是堆内存报错,当然也可能是其他内存空间不足引起的。

下面我们详细讲解 Java 对象的内存相关知识。 Java 对象内存布局简介

请思考一个问题: 一个对象具有 100 个属性,与 100 个对象每个具有 1 个属性,哪个占用的内存空间更大?

为了回答这个问题,我们来看看 JVM 怎么表示一个对象:

742441.png 说明 alignment(外部对齐):比如 8 字节的数据类型 long,在内存中的起始地址必须是 8 字节的整数倍。 padding(内部填充):在对象体内一个字段所占据空间的末尾,如果有空白,需要使用 padding 来补齐,因为下一个字段的起始位置必须是 4/8 字节(32bit/64bit)的整数倍。 其实这两者都是一个道理,让对象内外的位置都对齐。 一个 Java 对象占用多少内存?**

参考 Mindprod,我们可以发现事情并不简单: JVM 具体实现可以用任意形式来存储内部数据,可以是大端字节序或者小端字节序(Big/Little Endian),还可以增加任意数量的补齐、或者开销,尽管原生数据类型(primitives)的行为必须符合规范。

例如:JVM 或者本地编译器可以决定是否将 boolean[] 存储为 64bit 的内存块中,类似于 BitSet。JVM 厂商可以不告诉你这些细节,只要程序运行结果一致即可。

JVM 可以在栈(stack)空间分配一些临时对象。 编译器可能用常量来替换某些变量或方法调用。 编译器可能会深入地进行优化,比如对方法和循环生成多个编译版本,针对某些情况调用其中的一个。

当然,硬件平台和操作系统还会有多级缓存,例如 CPU 内置的 L1/L2/L3、SRAM 缓存、DRAM 缓存、普通内存,以及磁盘上的虚拟内存。

用户数据可能在多个层级的缓存中出现。这么多复杂的情况、决定了我们只能对内存占用情况进行大致的估测。 对象内存占用的测量方法*

一般情况下,可以使用 Instrumentation.getObjectSize() 方法来估算一个对象占用的内存空间。

想要查看对象的实际内存布局(layout)、占用(footprint)、以及引用(reference),可以使用 OpenJDK 提供的 JOL 工具(Java Object Layout)对象头和对象引用*

在 64 位 JVM 中,对象头占据的空间是 12-byte(=96bit=64+32),但是以 8 字节对齐,所以一个空类的实例至少占用 16 字节。

在 32 位 JVM 中,对象头占 8 个字节,以 4 的倍数对齐(32=4*8)。

所以 new 出来很多简单对象,甚至是 new Object(),都会占用不少内容哈。

通常在 32 位 JVM,以及内存小于 -Xmx32G 的 64 位 JVM 上(默认开启指针压缩),一个引用占的内存默认是 4 个字节。

因此,64 位 JVM 一般需要多消耗 30%~50% 的堆内存。

为什么,大家可以思考一下。 包装类型、数组和字符串*

包装类型比原生数据类型消耗的内存要多,详情可以参考 JavaWorldInteger:占用 16 字节(8+4=12+补齐),因为 int 部分占 4 个字节。所以使用 Integer 比原生类型 int 要多消耗 300% 的内存。 Long:一般占用 16 个字节(8+8=16),当然,对象的实际大小由底层平台的内存对齐确定,具体由特定 CPU 平台的 JVM 实现决定。看起来一个 long 类型的对象,比起原生类型 long 多占用了 8 个字节(也多消耗了 100%)。相比之下,Integer 有 4 字节的补齐,很可能是因为 JVM 强制进行了 8 字节的边界对齐。

其他容器类型占用的空间也不小。 多维数组*:这是另一个惊喜。

在进行数值或科学计算时,开发人员经常会使用 int[dim1][dim2] 这种构造方式。

在二维数组 int[dim1][dim2] 中,每个嵌套的数组 int[dim2] 都是一个单独的 Object,会额外占用 16 字节的空间。某些情况下,这种开销是一种浪费。当数组维度更大时,这种开销特别明显。

例如,int[128][2] 实例占用 3600 字节。而 int[256] 实例则只占用 1040 字节。里面的有效存储空间是一样的,3600 比起 1040 多了 246% 的额外开销。在极端情况下,byte[256][1],额外开销的比例是 19 倍!而在 C/C++ 中,同样的语法却不增加额外的存储开销。 String*:String 对象的空间随着内部字符数组的增长而增长。当然,String 类的对象有 24 个字节的额外开销。

对于 10 字符以内的非空 String,增加的开销比起有效载荷(每个字符 2 字节 + 4 个字节的 length),多占用了 100% 到 400% 的内存。 对齐(Alignment)*

让我们来看看下面的示例对象

class X { // 8 字节-指向 class 定义的引用
   int a; // 4 字节
   byte b; // 1 字节
   Integer c = new Integer(); // 4 字节的引用
}

我们可能会认为,一个 X 类的实例占用 17 字节的空间。但是由于需要对齐(padding),JVM 分配的内存是 8 字节的整数倍,所以占用的空间不是 17 字节,而是 24 字节。

当然,运行 JOL 的示例之后,会发现 JVM 会依次先排列 parent-class 的 fields,然后到本 class 的字段时,也是先排列 8 字节的,排完了 8 字节的再排 4 字节的 field,以此类推。当然,还会 “加塞子”,尽量不浪费空间。 Java 内置的序列化,也会基于这个布局,带来的坑就是加字段后就不兼容了。只加方法不固定 serialVersionUID 也出问题。所以有点经验的都不喜欢用内置序列化,例如自定义类型存到 Redis 时。 JOL 使用示例* JOL(Java Object Layout)是分析 JVM 中内存布局的小工具,通过 Unsafe、JVMTI,以及 Serviceability Agent(SA)来解码实际的对象布局、占用和引用。所以 JOL 比起基于 heap dump,或者基于规范的其他工具来得准确。 JOL 的官网地址为:

http://openjdk.java.net/projects/code-tools/jol/

从示例中可以看到:JOL 支持命令行方式的调用,即 jol-cli。下载页面请参考 Maven 中央仓库:

http://central.maven.org/maven2/org/openjdk/jol/jol-cli/

可下载其中的 jol-cli-0.9-full.jar 文件。 JOL 还支持代码方式调用,示例:

http://hg.openjdk.java.net/code-tools/jol/file/tip/jol-samples/src/main/java/org/openjdk/jol/samples/

相关的依赖可以在 Maven 中央仓库找到:

<dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>0.9</version>
        </dependency>

具体的 jar 可以在此搜索页面:

https://mvnrepository.com/search?q=jol-core

内存泄漏 内存泄漏示例*

下面展示的这个示例更具体一些。

在 Java 中,创建一个新对象时,例如 Integer num = new Integer(5),并不需要手动分配内存。因为 JVM 自动封装并处理了内存分配。在程序执行过程中,JVM 会在必要时检查内存中还有哪些对象仍在使用,而不再使用的那些对象则会被丢弃,并将其占用的内存回收和重用。这个过程称为”垃圾收集“。JVM 中负责垃圾回收的模块叫做”垃圾收集器(GC)“。 Java 的自动内存管理依赖 [GC]

很容易写个 Bug 程序,来模拟内存泄漏:

import java.util.*;
public class KeylessEntry {
    static class Key {
        Integer id;
        Key(Integer id) {
        this.id = id;
        }
        @Override
        public int hashCode() {
        return id.hashCode();
        }
     }
    public static void main(String[] args) {
        Map m = new HashMap();
        while (true){
        for (int i = 0; i < 10000; i++){
           if (!m.containsKey(new Key(i))){
               m.put(new Key(i), "Number:" + i);
           }
        }
        System.out.println("m.size()=" + m.size());
        }
    }
}

粗略一看,可能觉得没什么问题,因为这最多缓存 10000 个元素嘛!

但仔细审查就会发现,Key 这个类只重写了 hashCode() 方法,却没有重写 equals() 方法,于是就会一直往 HashMap 中添加更多的 Key。

请参考:《Java 中 hashCode 与 equals 方法的约定及重写原则》。

随着时间推移,“cached"的对象会越来越多。当泄漏的对象占满了所有的堆内存,GC 又清理不了,就会抛出 java.lang.OutOfMemoryError: Java heap space 错误。

解决办法很简单,在 Key 类中恰当地实现 equals() 方法即可:

@Override
public boolean equals(Object o) {
    boolean response = false;
    if (o instanceof Key) {
       response = (((Key)o).id).equals(this.id);
    }
    return response;
}

说实话,很多时候内存泄漏,但是可能功能是正常的,达到一定程度才会出问题。所以,在寻找真正的内存泄漏原因时,这种问题的隐蔽性可能会让你死掉很多很多的脑细胞。 一个 Spring MVC 中的实际场景*

我们曾经碰到过这样一种场景:

为了轻易地兼容从 Struts2 迁移到 Spring MVC 的代码,在 Controller 中直接获取 request。

所以在 ControllerBase 类中通过 ThreadLocal 缓存了当前线程所持有的 request 对象:

public abstract class ControllerBase {
    private static ThreadLocal<HttpServletRequest> requestThreadLocal = new ThreadLocal<HttpServletRequest>();
    public static HttpServletRequest getRequest(){
        return requestThreadLocal.get();
    }
    public static void setRequest(HttpServletRequest request){
        if(null == request){
             requestThreadLocal.remove();
        return;
        }
        requestThreadLocal.set(request);
    }
}

然后在 Spring MVC 的拦截器(Interceptor)实现类中,在 preHandle 方法里,将 request 对象保存到 ThreadLocal 中:

/**
 登录拦截器
/
public class LoginCheckInterceptor implements HandlerInterceptor {
    private List<String> excludeList = new ArrayList<String>();
    public void setExcludeList(List<String> excludeList) {
        this.excludeList = excludeList;
    }
    private boolean validURI(HttpServletRequest request){
        // 如果在排除列表中
        String uri = request.getRequestURI();
        Iterator<String> iterator = excludeList.iterator();
        while (iterator.hasNext()) {
        String exURI = iterator.next();
        if(null != exURI && uri.contains(exURI)){
            return true;
        }
        }
        // 可以进行登录和权限之类的判断
        LoginUser user = ControllerBase.getLoginUser(request);
        if(null != user){
        return true;
        }
        // 未登录,不允许
        return false;
    }
    private void initRequestThreadLocal(HttpServletRequest request){
        ControllerBase.setRequest(request);
        request.setAttribute("basePath", ControllerBase.basePathLessSlash(request));
    }
    private void removeRequestThreadLocal(){
        ControllerBase.setRequest(null);
    }
    @Override
    public boolean preHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler) throws Exception {
        initRequestThreadLocal(request);
        // 如果不允许操作,则返回 false 即可
        if (false == validURI(request)) {
        // 此处抛出异常,允许进行异常统一处理
        throw new NeedLoginException();
        }
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request,
        HttpServletResponse response, Object handler, ModelAndView modelAndView)
        throws Exception {
        removeRequestThreadLocal();
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
        HttpServletResponse response, Object handler, Exception ex)
        throws Exception {
        removeRequestThreadLocal();
    }
}

代码很长,只需要注意在 postHandle 和 afterCompletion 方法中,我们清理了 ThreadLocal 中的 request 对象。

但在实际使用过程中,业务开发人员将一个很大的对象(如占用内存 200MB 左右的 List)设置为 request 的 Attributes,传递到 JSP 中。 JSP 代码中可能发生了异常,则 Spring MVC 的 postHandle 和 afterCompletion 方法不会被执行。 Tomcat 中的线程调度,可能会一直调度不到那个抛出了异常的线程,于是 ThreadLocal 一直 hold 住 request。

然后随着运行时间的推移,把可用内存占满,一直在执行 Full GC,但是因为内存泄漏,GC 也解决不了问题,系统直接卡死。

后续的修正:通过 Filter,在 finally 语句块中清理 ThreadLocal。

@WebFilter(value="/*", asyncSupported=true)
public class ClearRequestCacheFilter implements Filter{
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
            ServletException {
        clearControllerBaseThreadLocal();
        try {
            chain.doFilter(request, response);
            clearControllerBaseThreadLocal();
        }
    }
    private void clearControllerBaseThreadLocal() {
        ControllerBase.setRequest(null);
    }
    @Override
    public void init(FilterConfig filterConfig) throws ServletException 
    @Override
    public void destroy() 
}

这个案例给我们的教训是:可以使用 ThreadLocal,但必须有受控制的释放措施,一般就是 try-finally 的代码形式,确保任何情况下都正常的销毁掉了对象。(所以说,GC 其实已经帮我们处理掉了 99.99% 的对象管理了,不然我们会遇到更多类似问题。我在十年前做 C++ 开发的时候,深有体会。)

说明:Spring MVC 的 Controller 中,其实可以通过 @Autowired 注入 request,实际注入的是一个 HttpServletRequestWrapper 对象,执行时也是通过 ThreadLocal 机制调用当前的 request。

常规方式:直接在 controller 方法中接收 request 参数即可。不需要自己画蛇添足的去额外包装处理。

这也是我们一直推荐使用现有的框架和技术,或者别人的成功实践的原因,很多时候别人实践过的,特别是成熟的框架和项目,都是趟过很多坑的,如果我们从头造轮子,很多坑我们还是要一一趟过,这可能是不值当的。

内存 Dump 与分析

内存 Dump 分为 2 种方式:主动 Dump 和被动 Dump。 主动 Dump 的工具包括:jcmd、jmap、JVisualVM 等等。具体使用请参考相关工具部分。 被动 Dump 主要是:hprof,以及 -XX:+HeapDumpOnOutOfMemoryError 等参数。

更多方式请参考:

https://www.baeldung.com/java-heap-dump-capture

关于 hprof 用户手册和内部格式,请参考 JDK 源码中的说明文档:

http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/raw-file/beb15266ba1a/src/share/demo/jvmti/hprof/manual.htmlmozTocId848088

此外,常用的分析工具有: jhat:jhat 用来支持分析 dump 文件,是一个 HTTP/HTML 服务器,能将 dump 文件生成在线的 HTML 文件,通过浏览器查看。 MAT:MAT 是比较好用的、图形化的 JVM Dump 文件分析工具。 好用的分析工具:MAT 1. MAT 介绍** MAT 全称是 Eclipse Memory Analyzer Tools。

其优势在于,可以从 GC root 进行对象引用分析,计算各个 root 所引用的对象有多少,比较容易定位内存泄露。MAT 是一款独立的产品,100MB 不到,可以从官方下载:下载地址2. MAT 示例*

现象描述:系统进行慢 SQL 优化调整之后上线,在测试环境没有发现什么问题,但运行一段时间之后发现 CPU 跑满,下面我们就来分析案例。

先查看本机的 Java 进程:

jps -v

假设 jps 查看到的 pid 为 3826。 Dump 内存:

jmap -dump:format=b,file=3826.hprof 3826

导出完成后,dump 文件大约是 3 个 G。所以需要修改 MAT 的配置参数,太小了不行,但也不一定要设置得非常大。

在 MAT 安装目录下,修改配置文件:

MemoryAnalyzer.ini

默认的内存配置是 1024MB,分析 3GB 的 dump 文件可能会报错,修改如下部分:

-vmargs
-Xmx1024m

根据 Dump 文件的大小,适当增加最大堆内存设置,要求是 4MB 的倍数,例如改为:

-vmargs
-Xmx4g

双击打开 MemoryAnalyzer.exe,打开 MAT 分析工具,选择菜单 File –> Open File… 选择对应的 dump 文件。

选择 Leak Suspects Report 并确定,分析内存泄露方面的报告。

bd3d81d4-d928-4081-a2f7-96c11de76178.png 3. 内存报告*

然后等待,分析完成后,汇总信息如下:

07acbdb7-0c09-40a5-b2c3-e7621a36870f.png

分析报告显示,占用内存最大的问题根源 1:

345818b9-9323-4025-b23a-8f279a99eb84.png

占用内存最大的问题根源 2:

07bbe993-5139-416a-9e6d-980131b649bf.png

占用内存最大的问题根源 3:

7308f1b5-35aa-43e0-bbb4-05cb2e3131be.png

可以看到,总的内存占用才 2GB 左右。问题根源 1 和根源 2,每个占用 800MB,问题很可能就在他们身上。

当然,根源 3 也有一定的参考价值,表明这时候有很多 JDBC 操作。

查看问题根源 1,其说明信息如下:

The thread org.apache.tomcat.util.threads.TaskThread
  @ 0x6c4276718 http-nio-8086-exec-8
keeps local variables with total size 826,745,896 (37.61%) bytes.
The memory is accumulated in one instance of
"org.apache.tomcat.util.threads.TaskThread"
loaded by "java.net.URLClassLoader @ 0x6c0015a40".
The stacktrace of this Thread is available. See stacktrace.
Keywords
java.net.URLClassLoader @ 0x6c0015a40
org.apache.tomcat.util.threads.TaskThread

4. 解读分析*

大致解读一下,这是一个(运行中的)线程,构造类是 org.apache.tomcat.util.threads.TaskThread,持有了大约 826MB 的对象,占比为 37.61%。

所有运行中的线程(栈)都是 GC-Root。

点开 See stacktrace 链接,查看导出时的线程调用栈。

节选如下:

Thread Stack
http-nio-8086-exec-8
  ...
  at org.mybatis.spring.SqlSessionTemplate.selectOne
  at com.sun.proxy.$Proxy195.countVOBy(Lcom/****/domain/vo/home/residents/ResidentsInfomationVO;)I (Unknown Source)
  at com.****.bi.home.service.residents.impl.ResidentsInfomationServiceImpl.countVOBy(....)Ljava/lang/Integer; (ResidentsInfomationServiceImpl.java:164)
  at com.****.bi.home.service.residents.impl.ResidentsInfomationServiceImpl.selectAllVOByPage(....)Ljava/util/Map; (ResidentsInfomationServiceImpl.java:267)
  at com.****.web.controller.personFocusGroups.DocPersonFocusGroupsController.loadPersonFocusGroups(....)Lcom/****/domain/vo/JSONMessage; (DocPersonFocusGroupsController.java:183)
  at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run()V (TaskThread.java:61)
  at java.lang.Thread.run()V (Thread.java:745)

其中比较关键的信息,就是找到我们自己的 package,如:

com.****.....ResidentsInfomationServiceImpl.selectAllVOByPage

并且其中给出了 Java 源文件所对应的行号。

分析问题根源 2,结果和根源 1 基本上是一样的。

当然,还可以分析这个根源下持有的各个类的对象数量。

点击根源 1 说明信息下面的 Details >> 链接,进入详情页面。

查看其中的 “Accumulated Objects in Dominator Tree”:

b5ff6319-a5d9-426f-99ef-19bd100fd80a.png

可以看到占用内存最多的是 2 个 ArrayList 对象。

鼠标左键点击第一个 ArrayList 对象,在弹出的菜单中选择 Show objects by class –> by outgoing references。

6dbbb72d-ec2b-485f-bc8e-9de044b21b7d.png

打开 class_references 标签页:

28fe37ed-36df-482a-bc58-231c9552638d.png

展开后发现 PO 类对象有 113 万个。加载的确实有点多,直接占用 170MB 内存(每个对象约 150 字节)。

事实上,这是将批处理任务,放到实时的请求中进行计算,导致的问题。 MAT 还提供了其他信息,都可以点开看看,也可以为我们诊断问题提供一些依据。 JDK 内置故障排查工具:jhat jhat 是 Java 堆分析工具(Java heap Analyzes Tool)。在 JDK6u7 之后成为 JDK 标配。使用该命令需要有一定的 Java 开发经验,官方不对此工具提供技术支持和客户服务。 1. jhat 用法**

jhat [options] heap-dump-file

参数:

  • options 可选命令行参数,请参考下面的 [Options]。 heap-dump-file 要查看的二进制 Java 堆转储文件(Java binary heap dump file)。如果某个转储文件中包含了多份 heap dumps,可在文件名之后加上 <number> 的方式指定解析哪一个 dump,如:myfile.hprof32. jhat 示例*

使用 jmap 工具转储堆内存、可以使用如下方式:

jmap -dump:file=DumpFileName.txt,format=b <pid>

例如:

jmap -dump:file=D:/javaDump.hprof,format=b 3614
Dumping heap to D:\javaDump.hprof ...
Heap dump file created

其中,3614 是 java 进程的 ID,一般来说,jmap 需要和目标 JVM 的版本一致或者兼容,才能成功导出。

如果不知道如何使用,直接输入 jmap,或者 jmap -h 可看到提示信息。

然后分析时使用 jhat 命令,如下所示:

jhat -J-Xmx1024m D:/javaDump.hprof
...... 其他信息 ...
Snapshot resolved.
Started HTTP server on port 7000
Server is ready.

使用参数 -J-Xmx1024m 是因为默认 JVM 的堆内存可能不足以加载整个 dump 文件,可根据需要进行调整。然后我们可以根据提示知道端口号是 7000,接着使用浏览器访问 http://localhost:7000/ 即可看到相关分析结果。 3. 详细说明* jhat 命令支持预先设计的查询,比如显示某个类的所有实例。

还支持 对象查询语言(OQL,Object Query Language),OQL 有点类似 SQL,专门用来查询堆转储。 OQL 相关的帮助信息可以在 jhat 命令所提供的服务器页面最底部。

如果使用默认端口,则 OQL 帮助信息页面为:

http://localhost:7000/oqlhelp/

Java 生成堆转储的方式有多种:

  • 使用 jmap -dump 选项可以在 JVM 运行时获取 heap dump(可以参考上面的示例)详情参见:[jmap(1)]

  • 使用 jconsole 选项通过 HotSpotDiagnosticMXBean 从运行时获得堆转储。请参考:[jconsole(1)]

    在虚拟机启动时如果指定了 -XX:+HeapDumpOnOutOfMemoryError 选项,则抛出 OutOfMemoryError 时,会自动执行堆转储。

  • 使用 hprof 命令。请参考:性能分析工具——HPROF 简介:[https://github.com/cncounter/translation/blob/master/tiemao2017/20hprof/20_hprof.md] 4. Options 选项介绍 -stack,值为 false 或 true。关闭对象分配调用栈跟踪(tracking object allocation call stack)。如果分配位置信息在堆转储中不可用,则必须将此标志设置为 false,默认值为 true。 -refs,值为 false 或 true。关闭对象引用跟踪(tracking of references to objects),默认值为 true。默认情况下,返回的指针是指向其他特定对象的对象,如反向链接或输入引用(referrers or incoming references),会统计/计算堆中的所有对象。 -port,即 port-number。设置 jhat HTTP server 的端口号,默认值 7000。 -exclude,即 exclude-file。指定对象查询时需要排除的数据成员列表文件。例如,如果文件列列出了 java.lang.String.value,那么当从某个特定对象 Object o 计算可达的对象列表时,引用路径涉及 java.lang.String.value 的都会被排除。 -baseline:指定一个基准堆转储(baseline heap dump)。在两个 heap dumps 中有相同 object ID 的对象会被标记为不是新的。其他对象被标记为新的(new)。在比较两个不同的堆转储时很有用。 -debug,值为 int 类型。设置 debug 级别,0 表示不输出调试信息,值越大则表示输出更详细的 debug 信息。 -version:启动后只显示版本信息就退出。 -h,即-help。显示帮助信息并退出. 同 -h-J <flag>:因为 jhat 命令实际上会启动一个 JVM 来执行,通过 -J* 可以在启动 JVM 时传入一些启动参数。例如,-J-Xmx512m 则指定运行 jhat 的 Java 虚拟机使用的最大堆内存为 512 MB。如果需要使用多个 JVM 启动参数,则传入多个 -Jxxxxxx。 参考

  • [jmap 官方文档]

  • [jconsole 官方文档]

  • [性能分析工具——HPROF 简介]

  • [JDK 内置故障排查工具:jhat 简介]

文章列表

更多推荐

更多
  • Pharo敏捷人工智能-第一部分:神经网络
    Apache CN

  • Pharo敏捷人工智能-第二部分:遗传算法
    Apache CN

  • Pharo敏捷人工智能-# 第三部分:神经进化 第三部分:神经进化
    Apache CN

  • Azure数据工程指南-二十四、数据治理的权限 创建 azure 预览帐户,探索 azure 预览,探索词汇表,浏览资产,以编程方式使用预览,摘要,管理凭证和访问,创建扫描, 许多组织需要建立数据治理流程、标准和方法,并且已经能够使用内部 SQL Server 工具(如 Master
    Apache CN

  • Azure数据工程指南-二十二、Synapse 分析工作区 创建 Synapse 分析工作区,使用 Spark 探索样本数据,用 SQL 查询数据,用 SQL 创建外部表,摘要, 微软 Azure 数据平台的众多新增功能已经围绕许多类似的产品及其在现代 Azure 数据平台中的用途产生了兴奋和困
    Apache CN

  • Azure数据工程指南-二十三、数据块中的机器学习 创建 MLflow 实验,安装 MLflow 库,创建笔记本,选择性测井,自动记录,摘要, 寻求利用机器学习(ML)和人工智能能力的组织和开发人员花费大量时间构建 ML 模型,并寻求一种方法来简化他们的机器学习开发生命周期,以跟踪实验,
    Apache CN

  • Azure数据工程指南-二十一、将 Apache Spark 的 GraphFrame API 用于图形分析 安装 JAR 库,加载新数据表,将数据加载到数据块笔记本中,用顶点和边构建一个图,查询图表,寻找有图案的图案,用 PageRank 发现重要性,探索入度和出度度量,摘要,进行广度优先搜索,查找连接的组件, 图形技术使用户能够以图形的形式
    Apache CN

  • Azure数据工程指南-20 二十、部署 SQL 数据库先决条件,创建 Visual Studio SQL 数据库项目,安装 Visual Studio GitHub 扩展,导入 AdventureWorks 数据库,连接到 GitHub Repo 源代码控制,将
    Apache CN

  • Azure数据工程指南-十九、部署数据工厂更改 先决条件,创建 DevOps 持续集成构建管道,创建 DevOps 持续部署发布渠道,验证部署的数据工厂资源,摘要,Azure PowerShell 任务停止触发器,ARM 模板部署任务,Azure PowerShell 任务启动触发器
    Apache CN

  • Azure数据工程指南-十八、用于 Cosmos DB 的 Azure Synapse 链接 创建一个 Azure Cosmos DB 帐户,启用 Azure Synapse 链接,创建一个 Cosmos DB 容器和数据库,将数据导入 Azure Cosmos DB,在 Azure Synapse Analytics 中创建
    Apache CN

  • 近期文章

    更多
    文章目录

      推荐作者

      更多