Placeholder Image

字幕列表 影片播放

  • And welcome to my session.

  • I'm going to be talking about V8 in the real world, or more specifically in the native

  • script framework.

  • I'm Stanimira Vlaeva, I'm a software engineer and work on this cool open source project,

  • NativeScript.

  • And I'm with web technologies, and find me on Twitter, the best place.

  • Or in the karaoke after.

  • So, NativeScript.

  • Our main topic today after V8, of course.

  • What is it?

  • What is it?

  • How many people here have heard about NativeScript?

  • Awesome.

  • Okay.

  • NativeScript is a framework for building native mobile applications for Android and iOS using

  • web technologies.

  • Like Angular, Vue, or just plain JavaScript.

  • In short, it is a way to execute JavaScript in the mobile world.

  • And build mobile applications with it.

  • We will take a short overview of the architecture of the framework.

  • At the bottom, of course, we have Android and iOS.

  • Operating systems.

  • On top of that we have the NativeScript run times for Android and for iOS which provide

  • the 100% native API access.

  • But if you have had to build a native application for Android or iOS, you may have noticed the

  • way to do that is quite different.

  • The APIs are different and the way to build your user interface is different.

  • Everything is completely different because they are two different worlds.

  • That's why NativeScript provides a common abstraction for these APIs.

  • It is part of the framework and the NativeScript developer cans use that in order to build

  • layouts or build user interface or even style their applications with CSS and this layer

  • is written in JavaScript and you can use that so that you can have a single codebase and

  • have different applications for Android and iOS.

  • NativeScript also has a very light application framework which provides us with native bindings,

  • navigations and some other cool things.

  • And if you need something a bit more sophisticated while building your applications, NativeScript

  • also supports Angular and Vue JS.

  • Today we're gonna talk about the bottom most levels.

  • The deep stuff.

  • And more specifically, we're gonna talk about the Android runtime.

  • The two runtimes are quite similar.

  • And the biggest difference between them is that the Android runtime uses V8 under the

  • hood whereas the iOS run time uses another JavaScript engine.

  • JavaScript Core.

  • But the principle of how they work is quite similar.

  • We're gonna start by explaining how the native API access works.

  • As you make have guessed from the name ""NativeScript,"" this is kind of what we mostly brag about

  • because we have 100% API access.

  • And this is why you should be using NativeScript instead of whatever  anything else you choose.

  • The main advantage.

  • How it works.

  • We'll start with the look at the application package of our NativeScript application.

  • So, we have Android, some phone or some device that is running the Android operating system.

  • And the NativeScript application is just a regular Android application and which has

  • some NativeScript magic inside it.

  • And the first part of the magic is, of course, the JavaScript code that the NativeScript

  • developer wrote and shipped inside that application.

  • The JavaScript code is not cross compiled or converted or anything like that, it stays

  • JavaScript during the whole life cycle while the application is running.

  • We also have the NativeScript run time, both in the Java part.

  • We're going to talk about them shipped together inside the application.

  • And the last part, almost, is V8.

  • Why do we need to ship V8 inside an Android application?

  • Well, to execute JavaScript.

  • V8 is a JavaScript engine.

  • It executes JavaScript.

  • It is embedded in Chrome, Note, even Microsoft nowadays and of course in NativeScript.

  • It was developed by Google.

  • It was created from the Chrome browser and it's one of the fastest JavaScript engines

  • out there.

  • Another reason why we chose V8 is because it has a cool API that we can use and plug

  • into the runtime.

  • If you want to read a bit more about V8 and how it works, I highly recommend these two

  • resources.

  • The first one is a really amazing popup series which is a crash course just in time compilers.

  • And the other one is a video which is very recent.

  • It's called why the script?

  • And describes the optimizations under the hood while it executes your JavaScript code.

  • It's from the V8 team.

  • If you want to learn about modern JavaScript engines, these are two great resources to

  • get started.

  • The next part of the NativeScript magic.

  • The metadata generator.

  • This is one of the very, very valid JavaScript code inside of NativeScript.

  • But we have something that is not usually in the JavaScript language, right?

  • Android.

  • Where does this come from?

  • Well, let's imagine that when your computer, you have some native library.

  • For example, Android SDK.

  • And you use that inside your NativeScript application.

  • While your application is being built, NativeScript runs a special tool called the metadata generator

  • which traverses that native library and gets information about the APIs.

  • It gets information about all the global packages, about every single class, about how these

  • classes can be instantiated, about every method in these classes and what are the meta signatures.

  • Basically, it gets information how every single method and API can be used.

  • That is saved inside a compact runtime binary which is, again, shipped inside the application.

  • So, we have information about how we can create stuff in Java inside the metadata.

  • And the metadata of course is shipped together with the whole application as well?

  • And what happens at launch time?

  • We initialize V8 which can execute the JavaScript code.

  • We load the metadata from the files saved inside the application and we attach source

  • and callbacks.

  • And the callbacks are the most important part about embedding V8.

  • They are our way to plug into the JavaScript code and do all sorts of stuff.

  • Let's start by explaining some stuff about these callbacks and how they actually work

  • together with the metadata to provide access with the native APIs.

  • Okay, we have this expression, Android media recorder.

  • We are trying to execute that JavaScript code.

  • The NativeScript runtime has read the metadata and found out that there is an Android global

  • package.

  • That's why it has created a global object inside the running V8 instance for Android.

  • It also has attached some callbacks to that object.

  • Like the package getter callback so that when we query for Android.media, the NativeScript

  • runtime plugs in with that callback.

  • The callback will be executed.

  • And inside the callback the NativeScript runtime will try to find Android.media inside the

  • metadata.

  • It returns something, some information, for example, some information that Android.media

  • has some media recorder.

  • And it also has a package getter callback attached.

  • So, when that callback is called, we find the media recorder inside the Android media

  • package in the metadata.

  • And this time we return a constructer function because this is actually a class.

  • And why is this constructer function so important?

  • Well, because when it's invoked with new, it actually contains a constructer callback.

  • Again, attached by the NativeScript runtime.

  • And this is where the actual magic happens.

  • Because the NativeScript runtime creates a native Java object.

  • But how does that happen?

  • Well, we use JNA, Java native interface, and this is a bridge between V8 and the running

  • Android runtime.

  • So, we can save functions back and forth between the two.

  • So, we create a native object.

  • Then we create the JavaScript proxy object that we're going to discuss a bit later and

  • we return the proxy object to the JavaScript world.

  • If it's right to access something inside that proxy, well, actually this proxy object is

  • not very simple.

  • It's not a plain object.

  • It creates some callbacks.

  • Contains some callbacks as well.

  • So, when we try to access this random field, we know that this field exists in the Java

  • world so that we have attached a field getter callback.

  • And the field getter callback actually queries the original Java object.

  • But there is a slight complication here.

  • Okay, we can get the result from the Java world.

  • But the data type is different from the JavaScript data type, right?

  • So, Java run string is not something we can assign to a JavaScript variable.

  • And that is why there is a marshaling service.

  • To convert it from Java so to JavaScript and vice versa.

  • At this point, you would say, wouldn't that be terribly slow to convert everything?

  • Obviously, it will be, if it's to convert object, it's not a good idea.

  • This is another reason why proxies are quite useful.

  • So, for objects, we just create a plain JavaScript object which has the same methods with the

  • same signatures.

  • And the same members as well.

  • And inside that we have callbacks.

  • So, that when you call some method with the same name on the JavaScript object, the callback

  • will be called and the NativeScript runtime will call the original Java method for JNA.

  • And this is a very cheap operation.

  • Creating new JavaScript objects.

  • Instead of converting data.

  • If you call a method, same story.

  • A method callback is triggered.

  • We call the original Java method.

  • The result is marshallized again and returned back to the JavaScript world.

  • If we have arguments in that method, the arguments will be converted to Java data format.

  • And then they will be  the Java method will be called with deconverted arguments.

  • Okay.

  • Let's see just a quick overview of all these callbacks, if they are confused you so far.

  • We try to instantiate new object and assign that to a JavaScript variable.

  • We call the constructer callback.

  • If you want to create a new instance of the class through JNA.

  • The instance is returned.

  • And because it's an object, the NativeScript runtime creates a JavaScript proxy object.

  • Then we try to call some methods on that proxy.

  • We call actually the method callback without knowing that we are calling it.

  • Everything is hidden.

  • It happens behind the scenes.

  • But the method callback then calls the original Java method.

  • The result that we can get is returned through JNA and marshallized and returned back to

  • the JavaScript world.

  • That's all the communication magic that happens.

  • What you may be wondering at this point what happens with these objects.

  • Like we create JavaScript objects.

  • We also create Java objects.

  • They are collected in some way.

  • So, we actually have to take care of their life cycle.

  • And in JavaScript we don't have to manually manage the memory.

  • There is a garbage collector that runs.

  • And it's always to retrieve the memory of the unused objects.

  • It also has a nondeterministic nature.

  • We can't be sure when the garbage collector will run.

  • And the other kind of complication is that, well, the Android runtime also has a garbage

  • collector.

  • It's pretty funny.

  • So, we have two garbage collectors running.

  • We have objects in both worlds.

  • And that's one of the biggest challenges of the NativeScript runtime.

  • We have to kind of try to synchronize that.

  • We have to ensure that no object is collected if there is a living counterpart.

  • For example, if you create some Java object through JavaScript, and then try to access

  • it, if the Android garbage collector collected the native Java object, that sounds really

  • cool because you will try to access something is that doesn't exist, and the application

  • will crash.

  • Like, it will crash.

  • Yeah.

  • You're running a mobile application and it's not really cool user experience.

  • Okay.

  • In order to plugin into the life cycle, we use finalizer callbacks so that when the garbage

  • collector of V8 marks something that  for collecting, says that some object doesn't

  • have living instances anywhere and it should be collected, the finalize of the callback

  • will be called.

  • And this is the place where the script runtime is plugged into.

  • We have strong and weak references.

  • Let's see how these actually look like.

  • We have the same example as before.

  • First, we create the native object.

  • Then we create the JavaScript proxy.

  • And then the NativeScript runtime has two collections.

  • One for strong references and one for weak references.

  • When the objects are first created, we create a strong reference or a link, if you would

  • like to call that, between the two objects.

  • And if that's confusing, okay, the proxy lives inside V8.

  • The original object lives inside the Android runtime and the references live inside the

  • NativeScript runtime.

  • All right.

  • Time to collect stuff.

  • Some garbage collector runs.

  • We can't really say for sure if it's gonna be the V8 garbage collector or the Android

  • runtime garbage collector.

  • But say in this example that V8 will decide to collect the memory first.

  • So, there is no one in the JavaScript world using the JavaScript proxy recorder.

  • And that's why it's marked for collection.

  • But at this point the finalizer callback is called.

  • And the NativeScript runtime sees that there is a living strong reference.

  • That's why the strong reference is turned into a weak reference.

  • And we instruct V8 not to collect that object.

  • The next time when the Android garbage collector runs, it decides to mark the recorder object

  • for collection because no one in the Java world is using it.

  • And sees there's a weak reference.

  • And because it's a weak reference, this object will be collected.

  • So, let's say that at some time the V8 garbage collector runs again.

  • Well, now there is a weak reference.

  • And the weak reference doesn't point to anything.

  • And because we don't have anything out there in the Java world, this object can also be

  • collected.

  • It's marked for collection and now we can collect it.

  • But if we had two consecutive garbage collector collection runs inside V8 and we still had

  • a weak reference to a living object that wasn't created by the Java garbage collector, the

  • V8 object wouldn't have been collected as well.

  • So, this is a normal cycle.

  • And as you could imagine, there are some challenges that happen because we have two running garbage

  • collectors.

  • Well, we could get out of memory exceptions.

  • Usually the objects that were created in the Android application are not really big.

  • So, we wouldn't have that happening for a hello world application, right?

  • But the problem is that, of course, yeah, we have a few garbage collection cycles that

  • should be run in order for some memory to be retrieved back.

  • And if we create some big objects, this can cause problems.

  • Because the memory is not retrieved on time.

  • For example, we can have images.

  • And an image  let's say that this Java array in the Java world.

  • The Java array is quite big.

  • Whereas the JavaScript proxy is not so big.

  • It's actually just a plain object with some callbacks attached.

  • So, it actually looks like that, memorywise.

  • We have a lot of memory in the Java world.

  • We have a really plain JavaScript proxy.

  • And the Java  the Android garbage collector is actually dependent on the V8 garbage collector

  • in order to retrieve this huge chunk of memory.

  • So, at this point the V8 garbage collector, even if you have tens of thousands of these

  • small, plain proxies, it doesn't have pressure to be run.

  • Because we don't really take a lot of memory in the running JavaScript virtual machine.

  • So, V8 doesn't really have a reason to trigger garbage collection.

  • If that doesn't happen on time, well, we may cause out of memory exceptions.

  • Because we're taking too much space in the Java virtual machine.

  • Some solutions or more like strategies to overcome this.

  • Because there is no straightforward deterministic solution.

  • Because of the nature of the problem.

  • The first one, there is an API provided by V8 that lets us instruct V8 about the memory

  • that is allocated inside of it.

  • So, in our case we can say to V8, okay, the Android application that is running actually

  • uses this amount of memory.

  • And this memory is used because you have created some JavaScript objects.

  • And the JavaScript objects are still pointing to living instances in the Java world.

  • So, this should hint V8 to garbage collection more often because it's aware that there is

  • more memory freed.

  • I mean, it works in practice, but we can still get out of memory exceptions.

  • Another important thing, we are doing this internally inside of a NativeScript runtime

  • so the NativeScript developers don't have to use that.

  • And it is a technique used internally.

  • Another solution.

  • We can force garbage collection, of course.

  • We can say, V8, come on, run garbage collection.

  • Mark these objects as free to be retrieved.

  • Make these strong references weak.

  • Then we can run the Android garbage collector.

  • And then run the V8 garbage collector again.

  • This is not the best thing ever because it doesn't guarantee that the garbage collection

  • will be run.

  • It kind of schedules it or hints it that it will be run.

  • But we don't have a guarantee that it will be run.

  • And you don't have a guarantee that it will be run in that order as well.

  • And it's not the cheapest option out there.

  • You are checking the objects and seeing if they have living references.

  • It may have the opposite effect.

  • So, this is not the best solution ever.

  • You can do it.

  • It is some strategy, but we don't really recommend using that.

  • Okay.

  • Let's take a look at this again.

  • We have a strong reference.

  • What didn't have references?

  • And what if we had the control over things like this Java object can't be collected because

  • I'm not using it anymore, it can be collected.

  • I'm not using it in the JavaScript world.

  • Well, the NativeScript runtime releases a function, release native counterpart and we

  • need to run the object, and it basically destroys all these references.

  • So, we invoke that.

  • We basically instruct that we're no longer using this native object and it can be retrieved.

  • So, whenever the next Android garbage collector runs, it is no longer dependent on V8's garbage

  • collector.

  • It can mark this object and say retrieve it.

  • And as the last part of the presentation, something like a bit simpler.

  • What is the point?

  • Well, the JavaScript code in NativeScript is run and executed from a single thread.

  • Which actually happens to be the main user interface thread.

  • And if you see where I'm going, this can cause some problems with log and junk.

  • So, you can see some glitches while your mobile application is being used.

  • And this is not the best experience for a native mobile application as well.

  • So, first you know what is junk, probably.

  • It is the percentage of frames that are dropped while you are doing some calculations.

  • We're not gonna focus on that.

  • It's important to know that in the NativeScript application, if you are just building user

  • interface, you are creating native Android and iOS widgets.

  • So, you shouldn't experience junk in a native list view when scrolling, for example.

  • If you are creating animations, same thing.

  • You have many ways of creating in NativeScript, with Angular, CSS, with JavaScript.

  • But internally it's actually creating native applications.

  • So, you shouldn't have any problem while running animations.

  • The other thing that is commonly  we're commonly asked for.

  • If you're creating an HTTP request, the plugin that you're gonna use creates a background

  • thread in the Java world which wouldn't freeze the main UI thread.

  • But you may see some junk when you're executing CPUintensive operations.

  • And the same thing would happen if you are executing CPUintensive Java code in an Android

  • application.

  • What is the solution?

  • Well, worker threads.

  • Essentially background threads to unlock the main thread.

  • We don't have JavaScript memory sharing, but we have a way to communicate between the worker

  • thread and the main UI thread.

  • And the final thing, I'm going to ask you a question.

  • You have to be patient for 30 more seconds.

  • What is a worker thread in NativeScript?

  • Two hints.

  • Is it an isolate?

  • Isolate is V8's way to isolate and executes some memory  sorry  to isolate some memory

  • for a code that's being executed.

  • They can run in parallel and we don't have memory sharing.

  • Context.

  • One isolate can have multiple contexts.

  • We don't have member isolation and we can't run contexts in parallel.

  • Also, you have to explicitly specify the context that some code is being executed on.

  • Isolates or context?

  • Isolates.

  • Contexts.

  • Okay.

  • That's okay.

  • Isolates.

  • Okay.

  • So, isolate.

  • All right.

  • So, this was about NativeScript and V8.

  • If you want to meet me afterwards, you can find me in Twitter, and I'll be coming to

  • you.

  • And I also want to thank my colleague who helped me with this presentation.

  • And this is his handle on Twitter as well.

  • So, thanks a lot.

  • [ Applause ]

And welcome to my session.

字幕與單字

單字即點即查 點擊單字可以查詢單字解釋

B1 中級

將V8嵌入到現實世界中,由Stanimira Vlaeva撰寫|JSConf EU 2019 (Embedding V8 in the real world by Stanimira Vlaeva | JSConf EU 2019)

  • 3 0
    林宜悉 發佈於 2021 年 01 月 14 日
影片單字