Transcript
[ Music ]
>> Hello.
[ Applause ]
Welcome to optimizing
web content in your app.
My name's Jonathan Davis.
I'm the web technology's
evangelist
for Safari and WebKit.
Now let me get something
out of the way up-front.
I've had people tell me I
look like Edward Snowden,
but I promise you I am not him,
but with all the
satellites flying overhead,
they may be all out to get us.
That aside, I'm really excited
to show you some new things
that will help you
get more performance
out of web content in your app.
And we've all known for a long
time that performance is key
to a great user experience.
So if you're an app
developer that uses WebViews
So if you're an app
developer that uses WebViews
and JSContext in app today.
And you care about performance,
you're in the right place.
So we're talking
about performance.
And in this day and
age, what we really mean
by performance is battery life.
I mean battery life is the
ultimate limited resource.
It can be the difference in
making that one last phone call
or sending that last critical
file before the battery dies.
And performance really
matters to our users,
and they naturally choose
apps that don't slow them down
and don't drain their battery.
So what's we've all learned
together, from users' feedback
and from each other, is that
performance really matters
because battery life matters.
So this year we focused on
giving you better tools to find
and fix performance
issues in your web content.
Now we have tools for
apps like for Swift,
and Objective-C code,
like Instruments.
And we have Web Inspector
for web developers,
creating web pages and sites.
But don't think that
just because you choose
to use web technologies
in your app
to use web technologies
in your app
that you don't have any tools.
In fact, I'm going to show you
today that all the same tools
that are developed to help
people build websites work just
as well for web technologies
in apps.
So I want to start
today by showing you how
to connect Web Inspector to
your WebViews in JSContext,
so you have the right tool
ready to go when you need it.
Then you're going to learn
about some new features
that will help you understand
where time is being spent
in your JavaScript code and how
these new features will help you
find performance
issues lightning fast.
And if you've ever wished you
could see how memory is used
by your web content, we've added
new timelines to Web Inspector
that I can't wait to show you.
They're going to save you
so much time finding
memory growth issues.
All right?
So let's get started.
And the first thing
we're going to need
to do is connect Web
Inspector to our app content.
Now there are all
sorts of reasons
to use web technologies
in your apps.
It could be some modular
JavaScript in a JS context,
so it's easy to swap
out the logic
or add new modules
without re-compiling.
And some of you may
be using JSContext
with TVML in a tvOS app.
And another reason to
use web technologies is
when you want to
show web content.
Like a web page from
a third-party website.
A site you don't control.
And for that, you're probably
using Safari View Controller
and if you're not, you
may want to take a look
at last year's session
Introducing Safari
View Controller.
But if you're showing content
that you own or control
or can customize content that
happens to be written in HTML,
CSS or JavaScript, content that
augments your app experience.
WKWebView is the best choice.
And it's essentially a rectangle
where web content is
drawn into your app.
It was introduced in iOS
8 and OS 10 Yosemite.
And if you're still using
a WebView or UIWebView,
you're really going
to want to take a look
at upgrading to WKWebView.
In fact, upgrading to WKWebView
allows you to take advantage
of the Nitro JavaScript
Core engine
and the four-tier JIT compiler
for a nice speed boost.
and the four-tier JIT compiler
for a nice speed boost.
So if you want to learn
more about WKWebView,
I recommend taking a
look at the 2014 talk
that introduced the
modern WebKit API.
So last year we added some
great new features to WKWebView,
like load file URL,
custom user agent strings
and the WK website
data store API.
And today, with iOS
10 and macOS Sierra,
we've improved 3D-touch support.
And now we allow your app to
implement those sweet Peek
and Pop events in WKWebView.
Now like I said earlier,
just because you've chosen
to use web technologies
in your app,
doesn't mean you
don't have any tools.
In all of these cases, you can
connect Web Inspector to dig
into what your web content
or JavaScript is doing.
But before you can
use Web Inspector,
you'll need to enable
the develop menu.
So just load up Safari
Preferences and go
over to the Advanced
tab and at the bottom,
you'll see this check box.
And it says Show
Develop menu in menu bar.
Just give that a
click to check the box
and the Develop Menu will appear
in the menu bar for Safari.
Now to allow Web Inspector
to connect your iOS devices,
there's a setting
you need to turn on.
So in the Settings app,
on iOS, tap on Safari.
Then down at the
bottom, tap on Advanced
and toggle the Web
Inspector setting On.
Now you can connect your
device to your Mac and check
out the Develop menu in Safari.
Now something that's really
cool in the Develop menu,
something you may not
have ever noticed before,
is you can see a list
of devices attached.
There's an iPhone
connected, a MacBook Pro
and the simulators there.
And all you have to do
to attach to one of these
and start using the tools
for debugging is just
choose the Device menu.
And you get a list of all
the WebViews and JSContexts
that are running on the device.
And this Mac app here
doesn't even use WebKit.
It's using JavaScript.
And I can connect right
to it and use the tools.
Now for iOS, apps will
only show up when you build
and run them from Xcode.
But when we're talking
about a Mac app,
there's just one more
thing you got to do.
there's just one more
thing you got to do.
To protect the integrity
of your app,
we don't let just
anyone download your app
and use Web Inspector
to poke around your app.
So you'll need to
add this entitlement
to your app's Entitlements
File for local development.
You probably already
have an Entitlements file
but if you don't, it's
pretty easy to create one.
You just create a new plist
with a .entitlements extension,
set the code signing
entitlements path to that file
in Xcode's build settings.
So you add this while
you're developing
and then you take it back
out when you ship your app.
Then once you have this
entitlement, your device
and app will show up
in the Develop menu
and you can attach to it.
And it's easy -- just that
easy to connect Web Inspector
to your JSContext and WebViews.
Okay. So we're up and running
with Web Inspector and our apps.
Time to move onto some new
features in Web Inspector.
And I want to start with
profiling your JavaScript code.
So we've had a profiler in
JavaScript for a long time,
and this year we have a
much better one that's going
to be way more effective at
helping you quickly figure
to be way more effective at
helping you quickly figure
out where your most expensive
JavaScript is getting called.
And the reason is simple.
The new profiler uses
a sampling technique
that doesn't affect your
performance anywhere near
as much as before.
So we had a profiler in an
era before there was really
a compiler.
It was an interpreter.
But now we have this very
powerful four-tier JIT compiler,
and the right profiler for that
is really a sampling profiler.
And the sampling
profiler tells you
where time is being
spent in your code.
It helps you answer questions
like what code is
costing us the most time?
It samples the running
program every millisecond,
and it pauses execution
briefly to take a quick snapshot
of all the code that's running.
And it can also take samples
while running your code
with all four tiers
of a JIT enabled.
So that means it's sampling
at near the true
speed of your code.
And since handling breakpoints
can cause code de-optimization,
we temporarily ignore them.
So while you're profiling,
you get the truest
performance for your web app.
There isn't nearly the
same performance cost
to using the sampling profiler.
And that literally means
while profiling your code,
it can run at up
to 30 times faster.
It makes the whole process of
profiling your code much quicker
and easier, and you get much
more accurate data as a result.
This was such an exciting
development that our team,
our Web Inspector team, was
able to take advantage of this
to find places where we
could improve the speed
in Web Inspector itself.
All right.
So we have a sampling profiler.
Let's see how Web Inspector uses
it to help us find problems.
So there's a lot going on here,
but it's actually pretty
easy to break down.
And it's even more helpful
with code you're
already familiar with.
When we're talking about
profiling JavaScript,
what we really mean is
we're recording a timeline
for JavaScript in Events.
And this is the Events
view, and it shows a list
of separate discrete
JavaScript events.
In particular, this is for some
code that uses the D3 library,
In particular, this is for some
code that uses the D3 library,
so it's even helpful
for debugging code
and profiling code that's in
a library that you're using.
And each entry here is an
event where code is executed.
It's where a code enters
and exists the JavaScript
Core engine.
And that includes event
listener callbacks,
like these animation
frame entries
for request animation
frame handlers.
Or script evaluated entries
where we see JavaScript files
are loaded and first evaluated.
And we also have some
timing information here,
showing you the time cost
of code that's being run.
So if anything is more
than 10 or 15 milliseconds,
you're getting really
close to dropping below
that smooth 60-frames-per-second
performance.
Now the Events view is helpful
but there's another view
that we've added for you and
that's the Call Trees view.
If you've used other
profiling tools,
this ought to be
pretty familiar to you.
Just click this Menu and
switch to the Call Trees view.
And now it shows you
the accumulative time
for functions in the call stack.
And this is what we call the Top
Down view, and you can use it
And this is what we call the Top
Down view, and you can use it
to dig down through
the Call Tree
to uncover hot functions
spending lots of time.
But my favorite view
is the Bottom Up view.
It takes me right to the
hottest functions, the functions
that are sampled most often.
And this is the list of
the called functions,
and it's sorted by the
ones costing the most time.
So it inverts the Call Tree
so you can quickly compare
the function costs directly.
And you can see exactly
where most
of your time is being spent.
You can expand the entries and
go back up the path that leads
to the functions
chewing up all the time.
And this tells you when
and where your most expensive
code is getting called.
And to see this in action, I'd
like to invite my colleague,
Brian Burg, to the
stage for a demo.
[ Applause ]
>> Thanks Jon.
So the sampling profiler
is great
because it can take
really complex content
because it can take
really complex content
and still profile it and get
you really accurate information.
And you can make it even faster.
So to show this off, I've
got this iPad app I made.
It's called Satellite Tracker.
Let me get the display here.
So Satellite Tracker will show
you, right now or any time,
where the satellites
are overhead.
So you can choose
different places on Earth.
You can choose different
satellites.
Different times.
So that's great.
If you're worried about
satellites overhead and want
to put on a tinfoil hat when
overhead, this is a great app.
But there's a small problem
is that if we have a lot
of satellites overhead,
or pieces of a satellite
that blew up, in this case, the
frame rate's kind of choppy.
This is definitely not
60 frames per second.
It's jittering all over.
So we can use the
sampling profiler to figure
out what's going on
and why it's so slow.
So what we've got to
do is go over to Safari
and we go to that Develop menu.
Find the iPad here
and attach to it.
And the first thing
I want to do is go
over to the Frames view here and
just see where we are right now,
in terms of frame rate.
So let's just start recording.
I'll switch back to my iPad.
And start doing stuff.
It'll sort of spin around here.
Maybe a change the satellites.
Change the time.
Okay. Let's go back and see.
Okay. Let's zoom way out.
Okay. Wow.
Yeah. Our performance is
all over the place here.
So sometimes we're getting
60 frames per second here
on the left.
In the middle, it's just sort
of going all over the place
as we're changing the views.
And here we're sort
of going too slow.
So if we want to figure
out what's going on here,
we want to switch over
to the Events view,
we want to switch over
to the Events view,
and then we can click on the
JavaScript and Events Timeline
to focus in on that
sampling profiler data.
So as Jon showed before, the
Events view here is going
to show everything that
went into the run loop.
And in this case, it's an
animation or simulation,
so we're just rendering
frames over and over.
So it's not really helpful,
if we want to figure
out what's taking the most time.
So let's switch over to the
Call Trees view, and here we see
that Top Down Call Tree.
And this shows aggregated over
all those rendering frames
where we spent the most time.
And here we can expand this to
see that D3 has a Timer function
and that calls some of our
code, which draws a scene.
And you know we draw some things
in the scene like satellites
and the time and these things.
So this is great, if we want
to understand what
the code is doing.
But if we want to figure
out which functions
specifically are really hot,
it's better to go over
to the Bottom Up view.
So here we've listed all
the Functions regardless
of who called them.
And we can see that fillText
and our tangent [phonetic]
are our two hottest functions,
so why are we calling
our tangent?
We can expand out this row here
and see who's calling
our tangent.
So right here, we're
plotting some satellites.
It looks like we're
computing the transform
so we can draw this globe.
Okay. These things
seem pretty normal.
Maybe I can pull
out my math book
and make it a little faster.
Let's go up to fillText.
So and to refresh you, if
we go back to the app here,
we're drawing text on
the current time up there
and also for every data point.
So that makes sense.
But if we look really
closely here,
we're actually drawing
the time twice,
and that seems kind of strange.
So if we expand this out, we
can see who's calling this,
and it seems that we're seem
to be drawing two different
foreground scenes at once.
This is probably
not what we wanted.
So let's figure out
what's going on.
If it was the case that we were
drawing two foreground scenes,
then we're doing twice as
much work as we need to.
So over here, just to
refresh your memory,
we have this sort of flat map.
And then we have the
globe, which rotates.
So
let's go back into the code,
and figure out what controls
switch in-between these
two maps.
Maybe we messed up somewhere.
So we switch between
the two globes
when we change the location.
So here's updateLocation.
Okay. So when we have one map
up, we don't see the other one.
So that makes sense because here
we're adding the hidden class,
So that makes sense because here
we're adding the hidden class,
and that's just going
to make it not display.
And here for the globeMap,
we set running equal to true
when the whole thing is running.
And also in the place we're
showing it's not a global
projection, so that makes sense.
When we're showing the flat
map, we don't show the globe
when it's not running.
Up here for the flat map, it
seems like it's always running
if the UI is running at all.
So that's kind of strange.
Well let's go back to the
map and try something.
So we'll go up to our
[inaudible] data set here.
And if we go to Earth, it seems
to have a better frame rate
than if we just did
the globe map.
And well, that makes sense.
I think we're drawing two maps
when the globe map is active
but only one map when
we're doing the flat maps.
So if we go in here and
change this condition --
We want it to be the opposite.
Okay. So let's stop, and
see if this is the fix.
So go back to our iPad here.
Okay. This looks pretty smooth.
Let's go here.
Oh that's great.
Yeah, it looks really nice.
So let's go and check that
rendering timeline again and see
if it's 60-frames per second.
So I'll switch back
to Frames again
and start recording and yeah.
That's pretty nice.
I'm spinning the globe, and
it slows down a little bit.
But the steady state
seems to be --
okay, we're definitely
under 60-frames per second.
And over here we have
the bar, and if we're
under it, then we're in luck.
Okay. So now Satellite Tracker's
a lot faster, so we know exactly
when to put on our hat.
This is great.
So this is a small example
of how we can use a
sampling profiler to dig
into really busy content
and make it even faster.
Okay. Next.
Jon's going to tell us about
memory and allocations.
[ Applause ]
>> Thank you Brian.
So you can see that
profiling is fast.
It allows you to see the
true speed of your code
so you can get really
accurate data.
So give the JavaScript and
Events Timeline another look
and use it to find slow
callback handlers, slow timers
or slow script initialization
in the Events view.
And use the new Call Trees view
to see time cost, as they pile
up across the time
slice you select.
Remember that Bottom Up
is your new best friend.
It really helps you find the
best places to start optimizing.
Okay. So we've looked at
the new sampling profiler
and new JavaScript
Call Trees view.
and new JavaScript
Call Trees view.
I'm really excited that
we have better tools
for optimizing CPU time.
And we can give our users a fast
experience and save a bit more
of their battery life.
And this is great and
now we're going to move
on to the other side of
the performance coin,
figuring out where
the memory is going.
So you want to be efficient
with memory in your web content
because it's a limited resource.
Being memory efficient helps
your web content be able
to scale really well to
handle large data sets.
Plus, using lots of
memory degrades performance
and we don't want to do that.
and it can also bring
down your web content
and we really don't
want to do that.
Now the good news is if
you're going WKWebView,
it runs in a separate process,
so it won't bring
down your entire app.
But still, it's not a
great user experience.
So to help you with all of this,
we've added two new timelines
to Web Inspector and Safari 10.
When you fire up the
new Web Inspector,
the new timelines will
be off by default,
so you need to turn them on.
You just click the Edit button
and just above the Timelines,
you can now configure the
timelines you want to see.
So you can just work with the
ones you want to work with.
Just like in the
Instruments app.
So just toggle the new
timelines On, and you're ready
to record a new timelines.
But you probably don't want
to keep them all
running at the same time.
The sampling profiler
for the JavaScript
and Events Timeline has less
overhead but there's still some.
And the JavaScript Allocation's
Timeline will add some extra
process during garbage
collection
that can have a performance
impact.
Okay. So we're going to leave
the Memory Timeline turned On.
And when you record a timeline,
you get something like this.
You get this new
Memory Timeline graph
and it shows you how
memory has been allocated
across different
categories over time.
And there are a series
of charts and graphs
to help you understand
how memory is being used
and how it's being divided up.
The Breakdown chart here shows
you how memory is allocated
for JavaScript, for images,
for the layers that make
up your page, and for the rest
of the engine-related
page memory.
And the Max Comparison
chart helps you investigate
And the Max Comparison
chart helps you investigate
memory spikes.
So we have a high watermark here
and that helps you see
memory problems in the past.
And you can even isolate spikes,
by selecting a specific slice
or a specific range of time
around a spike in the timeline.
And then, you can use the
category breakdown below
to see what's contributing
most of the spike.
And each of these graphs here
are independently scaled.
So you can easily see
changes over time.
So if there's a spike in
JavaScript, it shows you places
where lots of new objects are
being created and referenced.
And that includes Objects like
string Objects and functions
and all the engine
data that supports them
like structure data
and compiled code.
So when you see usage
drop in JavaScript,
you're seeing garbage
collection reclaiming memory.
And images shows you the
memory allocated for images
that have been decoded
for display,
so that's the larger image
data, usually used for images
that are visible
in the viewport.
And layers is showing you
graphics layer memory,
And layers is showing you
graphics layer memory,
memory used for WebKit's
tile grid, compositing layers
and other engine layers.
Pages is everything else,
all the other things the
engine's keeping track of,
like the DOM and page
styles, fonts rendering data,
memory caches and
system allocations.
So this breakdown gives
you a great way to ensure
that memory use lines up
with your expectations.
If you have JavaScript-heavy
content,
you'd expect the
largest category --
that JavaScript is the largest
category in the Breakdown chart.
And you'll likely see
more changes over time
in the Timeline graph.
But for an image-heavy page
like a gallery, for example,
then the layers and images
categories would likely be the
largest, with more
changes over time.
So that's the Memory
Timeline, new in Safari 10.
And to complement the Memory
Timeline is the new JavaScript
Allocations Timeline.
It's one thing to see
memory growing over time,
but it's another
thing pinning it
down to see what actual
JavaScript Allocations happen.
down to see what actual
JavaScript Allocations happen.
And a very powerful tool
for this is JavaScript
Allocation Snapshots.
What's powerful about Snapshots
is that you get a snapshot
of a moment in time,
in the JavaScript heap.
And you can dig in to see
everything that's allocated.
But it's even more powerful
when you have two Snapshots,
and this allows you to go back
later and compare the two.
And comparing Snapshots is
one of the most powerful tools
for answering the question, am
I doing unnecessary allocations?
So to really make use of this,
you need multiple Snapshots.
So that's why, by default,
we take one every 10 seconds
and also at the beginning
and end of a recording.
So the Snapshots are
plotted on the Timeline,
so you can correlate them
to things happening
on other timelines.
I just have the others
turned off here for now.
And the Snapshots are listed
below with a few details,
like timing and size
of the heap.
Now, to dig into a
particular problem,
you'll often need a Snapshot,
both before and after
where you think a memory
issue is happening.
And there are three techniques.
You can rely on the automatic
ones, every 10 seconds.
Or you can take one yourself
by pushing the Take
Snapshot button.
Or you can do it from your code.
And really the easiest way
to zoom into an issue is
to modify your code a little.
You call the takeHeapSnapshot
API
and pass it a custom label
argument of really anything
that can help you
identify it later.
And again, you want a pair
of Snapshots, both before
and after the code you think
is causing the problem.
Now you could also use
this by taking a Snapshot
between doing some
work in a loop.
So just some quick
things to keep in mind
about the takeHeapSnapshot API.
Remember that Snapshots do
add some extra process during
garbage collection and that
can impact performance,
which you'll definitely notice
if your code is firing off
a lot of Snapshots rapidly.
You'll also want to capture
the differences before
and after code that's doing
some work or at some point
between work in a loop.
And don't leave this in.
I mean, if you leave this in,
for most customers
it'll be okay.
for most customers
it'll be okay.
But if anybody's running
Web Inspector, they're going
to be taking all
these Snapshots,
and you probably
don't want that.
So just remember to be sure and
take it out before you ship.
So what are these
Snapshots really show you?
Let's take a look.
You just click on the
Snapshot icon on the Timeline
or on the Arrow button of any
of the Snapshot list entries.
And you'll get this
list of Objects
that were allocated in the heap.
And we have two views
for Snapshots.
This is the Instances view, and
it shows you a list of Objects
in the heap, grouped
by their class.
And the other is the
Object Graph view.
And this is really an
overview of everything
and everything that's
owned by everything.
So if you're readily
familiar with the code,
this can be a useful
way to confirm things
or where they should be.
But actually the far more useful
view is back in the other one,
in the Instances view.
And it's powerful because you
can easily find Objects no
matter how deep-down the
property path they are.
And the Count here can help
you realize potential issues
And the Count here can help
you realize potential issues
when they don't meet
your expectations.
Like was I really expecting
over 4000 string Objects?
So you can expand the
Classes and see all
of the allocated
Objects of that class.
Then to figure out what
something is there's all these
different clues.
The Class is a clue.
Another is the actual
properties of the Object.
It's a really quick
way to know what it is.
But the easiest way to know
what an Object is is to hover
over this Object Identifier
here and you get this.
Look at this.
It literally shows you the
shortest path to the Object.
This is telling you exactly what
is keeping the Object alive.
It almost always gives
you answer you need,
especially in a world of
JavaScript's garbage collection.
This is the kind of
thing that cuts right
through the confusion.
But the most important feature
and really the entire point
of this is to be able to
compare two Snapshots.
Now watch this.
Once you've collected
some Snapshots,
you just click the Compare
Snapshots button here,
you just click the Compare
Snapshots button here,
and you select a
Baseline Snapshot
and a second to compare against.
And boom. You get a new
Comparison Snapshot to dig into.
Now this is a really big deal
because now we're only seeing
the Objects that are new
between two points,
between our two Snapshots.
Expanding the Object
Class group we can see all
of these Object Allocations.
And their previews are showing
their names and what looks
like some telemetry data.
And that's a clue that
these are Satellite Objects.
And the pop over here
shows that they're
in the Satellites
Array property.
Since this is a Snapshot
comparison,
these Satellite Objects
are all newly allocated.
And that's a big clue as
to what the code's doing.
So to show you all of these
new memory features in action,
I'd like to invite Brian back up
to the stage for another demo.
[ Applause ]
>> Thanks Jon.
So I gave Satellite Tracker
to my buddy, Ed, and he stayed
up all night playing with it.
And he had a lot of fun,
and he never got tracked
by the satellites.
But there's a problem.
Eventually it just
kept getting slower
and slower the longer it was up.
And to me that sounded like
you know classic memory.
The longer the thing is
open, the slower it goes.
So I want to look
into Satellite Tracker
with these new memory
tools to figure
out if we're leaking
some memory somehow.
So the first thing
I'm going to do.
Okay I've got iPad here.
I'm going to go back
to the Web Inspector --
And inspect the app.
And the first thing
I like to do,
when I don't really know what
the bug is, here is I want
to use the Memory Timeline.
And that's going to show
me sort of like an overview
of what's going on on the page.
So let's start recording.
I'll switch back, and I'm just
going to switch back and forth
between two satellites.
Maybe I'll add some
effects here.
Maybe I'll add some
effects here.
Okay, I'm switching
back and forth.
Okay. Let's see what's
in the Timeline.
So in the Timeline overview you
can see a stacked line graph
showing all the different
parts and the relative size,
but if you click here, you'll
get that more detailed view.
And so there's no
images on this page.
This is all canvas.
The layers is pretty flat.
The page sort of
goes up and down,
stuff's getting garbage
collected.
That's fine.
If you look more
closely at JavaScript,
you see that like you know some
stuff gets garbage collected
but overall, it's really
just going up over time.
And if we had this
running all night, yeah,
it would probably
go up a lot more.
So the next step here is to
start taking heap Snapshots
or allocation Snapshots
so we can figure
out what's being
allocated over time.
So to do that, we're going
to start a new recording.
So to do that, we're going
to start a new recording.
And one cool quick trick here
is you can do Shift Click
or Shift Space, and they'll
start a new recording
and not append to the old one.
Oh wait. I forgot to
change our Timelines here.
So let's put away Memory.
And let's put in
Allocations instead.
Okay. So let's start recording.
Go back to the iPad.
Here I added this little
takeHeapSnapshot button,
so I already added some
calls to the console
about takeHeapSnapshot
when we switch
between the two satellite
groups and some other actions.
So for this recording, I'm going
to rotate and then I'm going
to switch between two
satellites over and over.
We should look at
Spy Satellites.
That seems kind of relevant.
Okay. And you'll notice
it starts to stutter a bit
because we're taking
Snapshots of everything
in the JavaScript heap.
So this you know we're
making lots of Objects,
So this you know we're
making lots of Objects,
so this is going to slow
down your app a lot.
So it's important to not
take lots of Snapshots.
You want to take them
only on important times.
So here, you can see
these Ss in the box
and those are the
Snapshots we took.
So if we zoom in over here,
we can see that there's pretty
steady memory growth over time
as we start switching
between these satellite sets.
So if we want to investigate
this, like Jon said,
we need to start
comparing two of these
to see what's being
retained at the end.
So let's go between
Snapshot 9 and 11.
And right away, we can
see a bunch of stuff
that was allocated
between Snapshot 9 and 11
and is still alive right now.
So that's a pretty good sign
that it's being retained
and you know we probably
didn't mean to do that.
So we can start looking
at what these things are.
There're some Arrays.
It looks like we have
Arrays full of coordinates.
And you know we use coordinates
in lots of different places
in this app, but
if we hover here,
we can see the path
to these things.
So these seem to be saved
in trajectoryHistory,
which is what we use to
make those trails behind
the satellites.
So okay. That's fine,
but I don't think we
should still have this
trajectoryHistory for satellites
who are no longer showing.
That seems kind of like a bug.
We allocate some Objects too.
And this is sort of
strange because you know
between Snapshot 9 and 11,
we've already seen
these satellites before.
So I wouldn't expect that
we're making new Objects
for each satellite.
We should just use them
if we already fetched
the resources for them.
So and then here's a bunch of
coordinates and telemetry stuff,
so it seems like we might be
like re-parsing them
or something.
But I'm not sure, so
one thing I'd like to do
But I'm not sure, so
one thing I'd like to do
in this view is you know
we have lots of Objects.
You can't read all of them,
but what I like to do is find
something that's fairly unique.
And in this Snapshot here,
we have lots of Strings,
lots of Arrays, lots of Objects.
But there's only one
Promise that's retained
between these two Snapshots.
So I think if I want to debug
this, I should start looking
in our code to see where we're
using Promises because it seems
that that's being leaked
with some other stuff.
So we search for Promise.
Okay. There's D3 library, and
here's our code that uses it.
Okay. Let's go to this one.
Okay. So in the [inaudible]
I did,
we switched between
the satellites a lot
and then the code that's
called loadDataset.
So someone left comments.
That's nice.
So here it looks like
someone requests we change
the satellites.
So here we asynchronously
load that data
So here we asynchronously
load that data
for the satellites from the TRL.
When it comes back,
we're going to parse it,
and then we're going
to parse it some more
with the satellite
plotting library.
And then we're going to save
it to our list of satellites.
That's all well and fine,
but back in Inspector,
it seems that we're leaking
this Promise every time.
So if you look more
carefully, what if we switched
to a data set that we
already loaded before?
It seems like we're not
even checking for that case.
So if you look more carefully
up here, every time we switch
between the two satellites,
we're doing a new
network request.
So if we go to this Timeline,
it seems like we're requesting
the same debris field data
over and over.
And that makes sense
because if we go back
to the code, we request that.
Then we make a bunch of
Objects when we parse it.
And then we push it onto
an array of satellites,
and that thing never really gets
cleared as far as I can tell.
So it seems like
we're just doing a lot
So it seems like
we're just doing a lot
of unnecessary work
and then leaking it.
So I think what we can
do here is to check
if we already have this
parsed satellites Object.
Because this is a Promise,
if this thing already exists,
we can still call .then on it.
Since it's already solved,
next time we evaluate
the Promise reactions,
it's going to go through and
set the satellites onto the map.
So let's add a [inaudible] here.
And if this doesn't exist,
then we'll make it --
Okay. Let's stop and rerun
and see if this fixes this.
So we need to go
back and reattach --
And okay, here's our app, and
we're going to start recording.
And when we go back to the app,
I'm going to turn
on the Snapshots.
Turn on some effects.
And we'll go down south.
Okay. So Spy Satellites.
Science Experiments
by Satellites.
Science Experiments.
Okay.
So if we go back here, we're
seeing a lot less memory growth.
Maybe 1 megabyte,
instead of like 4 or 5,
so there might be some
more leaks in here.
But at the end of the Snapshot,
we have about as much memory
as when we started rendering
this thing in the beginning.
So I think we've fixed
that particular leak.
So this shows how we can use the
Allocations and Memory Timelines
to figure out where
we're leaking memory
in apps like this.
And it's great because like this
app has a lot of stuff going on.
But still, with the dipping
[phonetic] functionality,
But still, with the dipping
[phonetic] functionality,
we can really drill
down to what's changing
in some Timeline we care about.
Okay. So that's the end
of Satellite Tracker.
Back to you, Jon.
[ Applause ]
>> Thank you Brian.
You can see how it
really amazingly quick
and simple these new
Timelines make it
to zero in on memory issues.
So remember you want to use
the Memory Timeline to figure
out how memory is being used and
what's driving memory spikes,
so you have an idea
of where to go look.
And then, take multiple
Heap Snapshots
in the JavaScript
Allocations Timeline to zoom
into code that's driving
the memory growth.
Also, don't forget to get
rid of takeHeapSnapshot
from your code before shipping.
And remember the
performance impact
that the JavaScript
Allocations Timeline can have
while recording.
So that's a look at the new
Timeline instruments available
in Web Inspector with Safari 10.
I think you're really
going to love using them.
I think you're really
going to love using them.
And as I wrap this up,
I want to leave you
with some next steps to take.
I want to encourage you
to reconsider WKWebView,
if you've not made
the switch yet.
And turn on the Develop
Menu in Safari Preferences,
connect Web Inspector to your
app and start taking advantage
of these new features.
Save a ton of time using
them with the Bottom Up view
and Call Trees for the best
places to start optimizing.
The Memory Timeline to
quickly see how memory spikes
are happening.
And Heap Snapshots
to easily explore
and compare Object allocations.
And stay updated on features.
There's more this year in
Web Inspector and WebKit
that you can take advantage
of in your app's web content
to deliver great
in-app experiences.
Along with the features
Brian and I showed you today,
our team added some
other features
to Web Inspector
over this past year.
Quick Open will jump you right
into the resources
loaded with your page.
And Tail Call Stacks will now
show you Tail Call optimized
functions in the debugger.
And earlier this spring, we
shipped Safari 9.1 on OS 10,
And earlier this spring, we
shipped Safari 9.1 on OS 10,
and with an updated
Web Inspector with it.
And that shows the
Pseudo Elements
in the DOM Tree of
the Elements tab.
And there's also the new
visual style sidebar.
And if you aren't already aware,
Web Inspector is a developer
tool that's created as part
of the WebKit Open
Source Project.
WebKit is the web browser engine
that's used to power your apps
and drive WebViews
and JSContext.
And of course, it's also
the engine behind Safari.
In this past year, our teams
added great new features
to WebKit.
We hit 100% support for ES6.
We improved support for the
recommended IndexedDB Standard.
We also added Shadow DOM support
and WebDriver, CSS Variables
and the Picture Element.
So there's a lot going on and
as an open source project,
you can follow development
as it occurs.
Most of you here will want
to take advantage of this.
And if there are some of you
here that want to enhance some
of these, you have
that opportunity
because of the way WebKit
is an open source project.
because of the way WebKit
is an open source project.
So you can find out more
about the WebKit
Project on WebKit.org.
And the WebKit team, the
engineers behind WebKit,
blog about development work.
In fact, we have two
blog posts up now
that cover the Memory Timelines
and the Sampling Profiler
that we talked about today.
So if you want to
get more information,
you can dig in there.
We also have a feature
status page
that gives you at-a-glance
updates
for our web standards progress.
And there's also
links to downloads
for WebKit nightly builds
and our latest browser,
the Safari Technology Preview.
It's updated every
couple of weeks,
with an updated WebKit
engine, so you can try out new
and experimental features
in WebKit every two
weeks as it's improved.
Our teams pore a lot of work
into Safari WebKit
and Web Inspector.
And the Web Inspector
team was able
to use these new performance
features we showed you today
to find issues and
deliver faster performance
in Web Inspector itself.
I can't wait to see what
you'll do with them.
So for more information,
you can watch this session
and download the slides
at developer.apple.com.
There are lots of other relevant
sessions earlier this week
There are lots of other relevant
sessions earlier this week
as well as in past years.
You can find those on
developer.apple.com as well.
So on behalf of Brian, myself,
Safari and the WebKit teams,
thank you for being here.
enjoy the rest of WWDC.
[ Applause ]