卡顿和 ANR 问题一直是 Android 性能优化的重点问题,直接关系到用户体验。当主线程的消息执行耗时过长时,轻则出现不流畅,不跟手,重则有肉眼可见的卡顿感,最严重则是发生 ANR,系统会弹出弹窗提示用户等待或关掉程序,严重影响用户体验。
对于卡顿的监控,现有的方案大多是在消息执行前埋点,当消息耗时过长时进行抓栈操作,ANR 的监控则是通过监听 SIGQUIT 信号并判断进程状态,在 ANR 时拿到各线程堆栈及各类辅助信息来定位问题。这种抓堆栈的方案对于单点长耗时是有效果的,比如锁等待和死锁,但是对于由多个消息和函数耗时累积造成的 ANR,一次堆栈是无法定位问题的,所以经常会出现 ANR 堆栈不准的情况。
西瓜视频的 Android TOP 1 ANR 就是聚类到了 nativePollOnce,根据 ANR 前的消息调度耗时可以看到存在许多长耗时消息,这些长消息是造成 ANR 的根本原因,而不是真的阻塞在了 nativePollOnce。分析发现其实这部分问题都是由于抓栈时机滞后导致的,从感知到 ANR 到完成抓栈存在一定的时间间隔,很容易错过现场,而 nativePollOnce 是主线程中执行频率最高的函数,抓栈很容易落到这里,所以导致很多 ANR 都是 nativePollOnce 的堆栈。另外传统的监控方案对于多个耗时消息累积导致的 ANR 也很难做到有效监控,单次抓栈大概率无法明确问题。所以如果能拿到 ANR 前的一段时间的 Method Trace,那么就能看到主线程到底在做什么,进而准确定位问题。
Android 可用的 trace 工具有很多,如 Android Studio 的 Profiler(与代码内的 Debug.startMethodTracing 效果类似),Uber 开源的 Nanoscope,Facebook 开源的 Profilo,Systrace 也可以统计一些关键节点的耗时。按照各个工具抓取函数耗时的实现原理,可以分为两类:埋点型和采样型。
Systrace、Debug.startMethodTracing 和 Nanoscope 都属于埋点型,埋点型就是在函数执行的出入口记录时间和方法名,这样就能获取到程序运行时都执行了哪些方法和对应的耗时。
除了这三种方案外,直接通过字节码插桩对应用内方法加埋点也可以达到目的,但是插桩会导致性能损耗、占用内存以及包大小增加的问题,且无法支持 Framework 堆栈,这里不再详细讨论。
Debug.startMethodTracingSampling 和 Profilo 属于采样型,其原理是每隔一定的时间,抓取一次线程的堆栈,通过在程序运行时不断的抓取,最终组合形成 trace 数据。
采样抓栈的限制也比较明显,采样间隔过大,trace 的精度会变低,而采样间隔过小,性能损耗就会增大。优点是可控性强,大部分情况下短耗时函数我们并不关心,而合适的采样间隔可以保证只监控长耗时函数。
方案 | 原理 | 场景 | 性能 | 主要缺点 |
---|---|---|---|---|
Systrace | 埋点型 | 线下 | 中 | 无法抓取应用函数栈 |
Debug.startMethodTracing | 埋点型 | 线下 | 低 | 强制解释执行,性能差 |
Nanoscope | 埋点型 | 线下 | 高 | 只能在定制 ROM 上运行,不够通用 |
Debug.startMethodTracingSampling | 采样型 | 线下 | 低 | 需要挂起全部线程,性能差 |
Profilo | 采样型 | 线上 | 高 | 稳定性难保证,适配成本高 |
我们的预期是通过 Method Trace 工具,达到解决线上性能问题的目的。通过对现场 trace 文件的解析来解决 ANR 和卡顿堆栈聚合及归因不准确的问题,进而提升此类问题发现和解决的效率。通过前面的分析,可以看到现有的工具或多或少都存在一些问题,而且大部分工具的设计就是面向线下的,存在不同程度的性能损耗与稳定性问题,无法上线使用。线上用户的设备环境复杂,遇到的很多性能问题是无法在本地模拟复现的,所以理想的 trace 工具需要能够在线上使用。
满足上线标准的 trace 工具需要达到:高性能、高稳定、低侵入、兼容性强等要求,所以需要针对现有问题设计一套方案,解决掉现有方案的问题。
埋点型方案如果想监控到 Framework 方法,就需要深入到 art 虚拟机,通过一些 hook 手段去修改运行时逻辑,而且还要兼顾到机器码执行等等,难度与风险都是巨大的,以现有的手段来看,埋点型基本无法实现。而采样型方案主要存在的问题就是采样精度与性能损耗之间的矛盾,ANR 和卡顿监控主要是监控长耗时函数,采样精度可以放低,所以我们只要保证单次抓栈时间最短,就可以对性能影响最小,同时保证稳定性兼容性,即可达到目标。
采样型 trace 方案的核心就是抓栈,大家最熟知的抓栈方法,应该是 Java Thread 类的 getStackTrace 方法,下面根据这个函数分析下抓栈原理。
getStackTrace 内调用了 VMStack.getThreadStackTrace,传入抓栈的目标线程。
该函数是一个 native 函数,里面的工作分为三步:一是创建回调,内部会调用 native 层的 Thread::CreateInternalStackTrace 函数;二是调用 GetThreadStack 函数,传入 env,Java Thread 以及回调函数指针;三是将 GetThreadStack 的返回结果通过 Thread::InternalStackTraceToStackTraceElementArray 处理,得到 Java 层最终的返回值 StackTraceElement[]。
GetThreadStack 函数内,会先判断抓栈的目标线程和执行抓栈的当前线程是否是同一个线程,如果是的话,则可以直接运行传入的回调 fn;而如果要抓其他的函数栈,则要先将目标线程 Suspend,再执行 fn,抓栈完成后再将目标线程 Resume。因为按照虚拟机的 Java 函数栈的实现,抓栈是非线程安全的,在跨线程抓栈时,目标线程的函数栈发生变动,就会出现问题,所以跨线程抓栈时需要将目标线程挂起,保证安全。
fn 回调内执行了 Thread::CreateInternalStackTrace 函数,该函数创建了两个 StackVisitor:FetchStackTraceVisitor 和 BuildInternalStackTraceVisitor。StackVisitor 就是栈回溯器,方法内先通过 FetchStackTraceVisitor 回溯,最大深度为 256,因为正常程序运行栈深度超过 256 的情况很少,这样做能够节省创建数组的开销。通过 WalkStack 回溯获取结果后,如果栈的深度小于 256,则添加到 BuildInternalStackTraceVisitor 中,否则再通过 BuildInternalStackTraceVisitor 回溯一次。最终将记录的方法数组返回,完成回溯。
StackVisitor 的核心函数是 WalkStack,其内部主要是一个遍历函数栈的操作。每个线程都管理着各自的 ManagedStack,是一个链表结构,每一个 ManagedStack 节点又管理着 cur_shadow_frame_ 和 cur_quick_frame_,分别表示解释执行帧与机器码执行帧。
curshadow_frame 也是链表式结构,每个 ShadowFrame 节点对应一个 ArtMethod,通过遍历即可获取到对应的函数栈。
curquick_frame 则是 ArtMethod 指针数组,即 ArtMethod**,通过遍历计算偏移量 frame_size 来不断的得到对应的函数。
WalkStack 函数遍历 ManagedStack,每个 ManagedStack 节点只能存在一种 frame 类型,然后在循环里不断遍历 curshadow_frame 或 curquick_frame,每次得到一层函数栈,然后通过 StackVisitor 的虚函数 VisitFrame 来判断是否需要继续回溯。
art 里有许多 StackVisitor 的实现,在实现的 VisitFrame 内获取函数栈然后进行各种操作。比如上面提到的 FetchStackTraceVisitor 就在实现的 VisitFrame 内进行 ArtMethod 的筛选和记录等操作。
综上分析,抓取 Java 函数栈的核心函数就是 StackVisitor::WalkStack,art 内抓取函数栈的最终实现都是该函数。其实通过阅读 Profilo 的源码也可以发现,其抓栈的核心函数就是通过指针+偏移量的方案实现了 WalkStack,写死了大量偏移,并做了很多适配工作。这样做虽然可以实现功能,但稳定性就会变差,维护成本也会增高。Profilo 的开源版本截至目前仅适配到了 Android 9,适配 Android 10、11 和 12 的 32&64 位架构又是极大的工作量,而且厂商定制 ROM 若更改了任何一处源码,导致偏移量发生变化,那么轻则功能不可用,重则发生 Crash。
既然自己实现栈回溯十分复杂且风险较大,那就直接用系统的函数,通过 xDL 查找符号表拿到 WalkStack 的函数指针直接调用。
该函数只有一个 bool 型入参,但该函数是成员函数,编译后会增加一个入参即 this 指针,而且 StackVisitor 还包含虚函数 VisitFrame,所以需要构造出一个实现 VisitFrame 的 StackVisitor。最简单的方案就是利用内存布局的特性,直接复制 StackVisitor 的代码到工程内,使用时直接 new 即可。
通过阅读源码发现,StackVisitor 的结构包含了如图中“CodeInfo”和“BitTableRange”这类变量,若直接采用复制的方案,一是复杂,二是厂商修改可能性大。通过上面对 WalkStack 的原理分析,回溯中的核心变量是 curshadow_frame 与 curquick_frame,至于行号在 trace 中是非必须的,解析行号只会徒增成本,所以作为调用方其实只关心前四个变量,后面的变量我们并不关心其值,只要保证运行时有足够的内存 get/set 即可。
这里采用一种取巧的方案,自己构造的 StackVisitor 中,只写出虚函数和前四个核心变量,而后面的内容,使用长度足够的 char 数组占位,这样在运行时就有了足够的内存读写其他的变量,而且只使用前四个变量可以降低厂商定制带来问题的概率。
然后再实现一个继承自 StackVisitor 并实现了 VisitFrame 的函数,在其内部通过 curshadow_frame 与 curquick_frame 获取到每一帧对应的 ArtMethod,这样即可完成 StackVisitor 的构造。
通过函数指针调用 WalkStack,即可完成一次栈回溯的操作。
在 Callback 内将每一层的 ArtMethod* 保存到数组中,完成堆栈的记录。
实现了抓栈能力后,还需要选择暂停目标线程的方式,有两种方案,一是通过发送信号来实现中断,二是通过虚拟机的 Suspend 函数来实现挂起。
信号机制无论是实现还是性能都是要略优于挂起机制的,但在实际开发过程中,发现如果在信号回调中调用系统的 WalkStack,运行一段时间后会出现各类 Crash。
比如这一类在 WalkStack 内部调用 GetOatQuickMethodHeader 时的崩溃,x0 寄存器即该函数的 this 指针,ArtMethod 的值为 0x8 导致的非法指针问题。
还有 Didn't find oat method index for virtual method 的 abort 异常,以及各种各样的其他崩溃,堆栈不固定,但基本都是在解析机器码函数栈时发生了异常。
通过分析发现,这些崩溃在正常情况下是不应该出现的,经过测试,在非信号回调中频繁调用 WalkStack 确实没有发现任何异常。经过深入分析与阅读源码,推测是发生了重入问题。由于信号的机制,信号回调会在中断后调用,而目标线程被中断时,无法保证指令的位置,所以如果是中断在机器码函数栈记录过程中,而我们又在回调中执行 WalkStack,就会发生不可预期的崩溃。而机器码函数相关的代码十分复杂,暂未找到该问题的有效解决方案。既然信号机制的问题暂时无法规避,所以选择尝试线程挂起方案。
ThreadList 的 SuspendThreadByPeer 函数,可以通过传入目标线程的 Java Thread 对象的 jobject 完成对目标线程的挂起,刚好可以满足需求。但是该函数是成员函数,调用还需要拿到 ThreadList 指针,ThreadList 是全局唯一的,定义在 Runtime 中,翻遍源码也未找到安全可靠的获取方式,最终只能通过写死偏移的方式获取。
ThreadList 在 Runtime 中的位置比较靠后,单纯的写死偏移还是风险太高,所以决定采用偏移+校验的方式来保证读到的内存是 ThreadList。Android 10 的 Runtime 结构中,threadlist 再向后增加 4 个指针长度,即是 JavaVMExt,JavaVM 可以在 JNI_OnLoad 时获取到,所以只需要在初始化时对各个版本的偏移进行 JavaVM 的校验,就可大大降低出问题的概率。
获取到 ThreadList 和函数指针后,在栈回溯的前后调用 Suspend 和 Resume 即可完成一次跨线程栈回溯。在 Suspend 和 Resume 之间,仅执行 WalkStack 的操作,堆栈记录和其他操作则放在 Resume 后执行,保证线程被挂起的时间足够短,这样可以确保性能最优。
线程挂起机制经过实践验证可以满足现有需求,实际运行中也未发现无法攻克的问题。另外线程挂起相较于信号机制,可以将大部分操作放到 Resume 后执行,而信号机制记录堆栈等操作需要在回调内做,额外增加耗时,所以最后决定选用线程挂起方案。
调查 ANR 和卡顿问题,大部分时候只对主线程采样抓 trace 即可达到目的,但还是有些问题需要结合子线程的 trace 分析。
Android 提供的采样 trace 最大的问题是每次都需要挂起所有线程,等待全部线程抓栈完成后,才恢复线程,而且这样造成了很多无意义的等待。
为了避免性能损耗问题,采用了分组的方式来设计多线程 trace:主线程单独由一个采样线程抓栈,无其它干扰,子线程分布到一定数量的采样线程中,比如设置五个采样线程,目标子线程会被添加到这些采样线程中,每个采样线程对一定数量的子线程进行采样抓栈,且每个目标子线程通过 SuspendThread 独立挂起,而不是 SuspendAll,互不影响。
很多时候仅有 Method Trace 定位问题依然较为困难,一个函数的耗时可能有很多原因,知道耗时,还要知道为什么耗时,比如在等锁,进行 I/O,Binder 等情况。Java 层的 I/O 和 Binder 通信可以通过函数栈看出来,而锁信息就很难通过函数栈确定了。
在 ANR 日志中,经常可以看到类似的锁信息,能够非常清楚的看出主线程在等待子线程释放锁。如果能拿到这个锁信息并体现到 trace 中,就可以很轻易的知道当前主线程的阻塞原因。
ANR 日志是通过 DumpForSigQuit 输出的,获取 Java 函数栈是通过 Thread::DumpJavaStack 方法,该函数的核心代码是通过 StackDumpVisitor 进行栈回溯。
StackDumpVisitor 继承自 MonitorObjectsStackVisitor,最终也是继承自 StackVisitor,特点是实现了很多有关锁信息的函数。
在其实现的 VisitFrame 函数中,会通过 Monitor::FetchState 拿到当前线程的锁信息,还可以拿到当前线程的状态,添加到堆栈中。所以只要在自实现的 VisitFrame 中,通过函数指针直接调用 Monitor::FetchState 函数,就能拿到锁信息。
在栈回溯时调用一次 FetchState,如果当前线程是 block 状态就保存拿到的锁指针和持锁的线程 id,与堆栈一同记录。
完成一次抓栈后,为了得到完整的 trace,需要将本次抓栈的结果与前一次抓栈的结果进行对比计算,得出函数出栈入栈的结果进行记录。方案为从栈底到栈顶遍历新旧两次堆栈,找到不相同的函数,理论上存在四种情况:
数据记录采用 ring buffer 的结构,每次入栈或者出栈事件用两个 uintptr_t 表示,记录时间戳与对应事件值。当入栈时,事件值为 ArtMethod 指针地址值;当出栈时,事件值为 0。
为了保证抓栈的高性能,在采样过程中,不进行堆栈的反解,即不进行 ArtMethod 转为 string 的过程,在 dump trace 时统一通过 ArtMethod 的 PrettyMethod 方法进行反解。
Sliver 使用了 Nanoscope 的文件格式记录 trace,易于解析,且能够清晰的表示栈结构。每一行开始的时间戳为纳秒级,表示事件发生的时间;时间戳后跟着对应时刻发生的事件,POP 表示出栈行为,其它表示对应函数的入栈行为。
Sliver 在 Java 层提供 start 和 dump 方法,start 函数会传入目标线程,采样间隔等信息,在 native 层通过 pthread_create 创建采样线程 SamplingThread,采样线程循环执行 Suspend->WalkStack->Resume 对目标线程进行抓栈,抓栈完成后对比函数栈得到函数的出入栈信息,写入 RingBuffer 中。在 Java 层调用 dump 时,会读取 RingBuffer,通过 PrettyMethod 方法将 ArtMethod 转为函数名,输出到文件中。
拿到文件后可以通过脚本处理成 perfetto 的格式,在 perfetto trace viewer 上展示。
对单次栈回溯的耗时进行埋点,设置采样间隔为 10ms,接入到西瓜视频,程序运行中取连续的 1w 次耗时数据,在高端机与低端机分别进行分布统计,横轴为单次回溯耗时区间,单位 us,纵轴表示落在对应区间的样本量。
在高端机上,可以看出,约 93%的栈回溯耗时是小于 40us 的,1w 次耗时的平均值为 23.6us。在 10ms 采样间隔的情况下,按单次栈回溯耗时 25us 计算,每秒耗时增加 2.5ms,性能损耗为 2.5‰。
在低端机上,性能会稍差一些,但约 92%的栈回溯耗时是小于 100us 的,1w 次的平均值为 43.2us,在 10ms 采样间隔的情况下,按单次栈回溯耗时 50us 计算,每秒耗时增加 5ms,性能损耗为 5‰。实际使用中,在低端机上可以适当放大采样间隔,比如设置为 20ms 来降低损耗,一定程度的放大采样间隔对调查问题能力并无影响。
当然这样计算出的是理论损耗,受到 Suspend 机制的影响,在 Resume 后线程不会立即运行,而是处在等待调度的状态,所以实际运行中损耗是要大于理论值的,但总体可控制在 1%以内。
Sliver 在线上灰度验证时,出现了 ThreadSuspendTimeout 的崩溃,这类问题在线上经常发生在 GC 时的 SuspendAll 函数,而采样抓栈调用挂起太过频繁,增大了问题出现的概率。
在通过 SuspendThreadByPeer 挂起线程时,会在循环内不断的调用 ModifySuspendCount 尝试挂起目标线程,每次挂起失败会检查一下是否超时,如果超时则会抛出 FATAL 异常。观察这段代码,可以发现 FATAL 后的逻辑是根本不会走到的,因为程序在 ThreadSuspendByPeerWarning 时会 abort 终止掉,那么后面的代码设计的就没有意义,比较奇怪。
通过搜索代码,发现了另外一个函数 SuspendThreadById,该函数的代码与 SuspendThreadByPeer 基本相同,功能同样是挂起线程,只不过是通过线程的 id 来挂起。而有一点不同是当线程挂起超时, ThreadSuspendByIdWarning 的 log 等级由 FATAL 变成了 WARNING,也就是仅打出 log,并不会崩溃,这样后面的逻辑也可以执行到了。在发现了这点后,把支持 SuspendThreadById 的版本均替换成了该函数,这样在发生挂起超时,不会发生崩溃,仅会丢失采样数据。在不支持 SuspendThreadById 的版本上,依然使用 SuspendThreadByPeer,通过采样时 hook 将此处的 log 等级替换成 WARNING 来避免崩溃。
除了这个问题以外,还陆续解决了一些由于自身代码或兼容适配产生的问题,另外由于少部分能力还是使用了风险较高的写死偏移,所以这部分代码统一使用 signal handler + siglongjmp 进行保护,在出错时及时停止采样。
通过在西瓜视频上的迭代,目前的版本已经很少有相关稳定性问题发生了,目前在西瓜视频的内测和灰度阶段全量开启,崩溃率小于十万分之一。
工具实现大部分都使用了符号查找的方案调用系统函数,在 Android 系统的升级中无需庞大的适配工作量,且针对 32 位和 64 位架构也没有过多需要注意的点,维护成本较低。
Sliver 开发完成后,很快就在西瓜视频的卡顿及 ANR 治理中进行了实践。在应用启动后开启 Sliver,在发生卡顿或 ANR 时 dump trace 并随着异常一同上报。
例如这次 ANR 问题,ANR 堆栈体现成 nativePollOnce,但是通过调度消息耗时记录可以看出在感知到 ANR 前有一个耗时 5.8 秒的消息,这个消息应该是造成 ANR 的根本原因,但传统的 ANR 监控手段很难看出这 5.8 秒是在做什么。
而通过随 ANR 上报的 trace 文件可以直观地看出这段时间的函数执行耗时,直接定位到问题原因。
对于卡顿问题,在检测到消息执行超过一定阈值时,dump trace 并直接在端上进行分析,快速找出卡顿点,拿到堆栈后与 trace 文件一同上报。在端上分析时,会根据设置的函数阈值对目标消息的 trace 进行过滤,每一层会留下大于阈值且耗时最长的函数,组成卡顿栈进行上报。经过实践验证,这套方案能够找到卡顿中真正的耗时函数,卡顿聚合准确度大大提升,降低了卡顿的排查成本。
trace 工具的原理虽然不算复杂,但如果想做到极致的性能与稳定性,还是非常困难的。大部分 trace 工具都是在功能或性能或稳定性上做了取舍,Sliver 也是一样,在保证了性能和稳定性的前提下,必然是失去了部分的能力。
Sliver 目前在全线程 trace 的精准度上有一些不足,由于没有使用信号回调的方案,所以必须要单独开启线程进行抓栈,而在抓全线程 trace 的情况下,子线程被分组添加到同一个采样线程进行抓栈,所以在额外创建线程数与子线程 trace 精准度之间存在无法调和的矛盾,只能尽量寻找平衡。我们也在探索如何稳定的切换到信号方案或探索更优秀的方案,优化这部分的能力。
众所周知,ANR 时函数执行变慢并不一定是这些函数在代码层面存在问题,CPU 负载过高,内存资源紧张等情况也会造成性能变差。Sliver 提供了线程状态和锁信息来辅助定位一些特殊情况的问题,但对于 CPU 占用和内存情况并没有体现在 trace 中,所以提供更完善的辅助信息也是后面的目标之一。
Sliver 目前具备了抓 Java Method Trace 的能力,但对于西瓜视频这类 native 代码较多的应用来说,经常会遇到 jni 方法调用耗时过长的情况,这时只依靠 Java 堆栈是无法定位问题的。所以后续会考虑通过增加 native 栈或其他方式来填补这方面的能力空缺。
Android 卡顿和 ANR 的治理是一个长期的事情,需要有完备的调查手段和方法论。业内针对性能问题的排查产出了如 Nanoscope 和 Profilo 这类优秀的工具,正是这些工具的出现,让我们能够开阔思路,吸取优点,拟补不足,在 trace 工具建设上做探索与尝试。
Sliver 目前已经投入到西瓜视频的卡顿与 ANR 治理中,实践中基于此工具开发了一些辅助能力也沉淀了一些方法论,帮助我们解决了很多问题,其能力经过了西瓜视频的长期迭代验证。
Sliver 的开源计划已经提上了日程,我们希望能够将此工具分享给更多的有性能治理需求的应用,同时也可以集思广益,探索出更优秀的方案。
Sliver 的技术方案还有很大的优化空间,后续会在此基础上继续探索,不断完善,努力打造性能更好,功能更全,更加稳定的 trace 工具。
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/LW3eMK9O2tfFtZcu5eqitg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。